The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
Changes 086
DESIGN 56
MANIFEST 15862
META.yml 411
Makefile.PL 020
README 313
ToDo 04
bin/jemplate 41724700
inc/Module/Install/Base.pm 2634
inc/Module/Install/Can.pm 1110
inc/Module/Install/Fetch.pm 66
inc/Module/Install/Include.pm 44
inc/Module/Install/Makefile.pm 1841
inc/Module/Install/Metadata.pm 93346
inc/Module/Install/Scripts.pm 3817
inc/Module/Install/TestBase.pm 25
inc/Module/Install/Win32.pm 33
inc/Module/Install/WriteAll.pm 323
inc/Module/Install.pm 27114
inc/Test/Base.pm 437
inc/Test/Builder/Module.pm 2617
inc/Test/Builder.pm 452866
inc/Test/More.pm 195269
lib/Jemplate/Directive.pm 4124
lib/Jemplate/Grammar.pm 32323349
lib/Jemplate/Runtime/Compact.pm 33
lib/Jemplate/Runtime.pm 565832
lib/Jemplate.pm 1027
t/9bug/37539-intermediate-instantiation.t 034
t/9bug/37540-iterate-over-empty-object.t 035
t/9bug/37570-list-for-lists.t 032
t/9bug/37895-inconsistent-jemplate-version.t 012
t/9bug/43809-capture-implementation.t 034
t/9bug/53453-make-Jemplate-slice-like-TT-slice.t 051
t/9bug/53454-array-range-operator.t 052
t/9bug/53967-compiler-double-dot-path-fail.t 012
t/9bug/56965-DIV-operator-broken.t 036
t/a.js 345596
t/assets/jt/a/.hidden/apple 00
t/assets/jt/a/b/c/.hidden 00
t/assets/jt/a/b/cherry 03
t/b.js 345596
t/block.t 17
t/check.js 346630
t/macro.t 056
t/pairs.t 038
t/quoted.t 11
tests/Makefile 1167
tests/README 054
tests/Test/Jemplate.js 370
tests/basics.t.js 760
tests/bin/daemon 0125
tests/bin/prepare-var-jt 025
tests/bin/pull-jemplates 025
tests/bin/render-template 036
tests/bin/test-run 029
tests/bugs.t.js 560
tests/config.yaml 48
tests/default.t.js 240
tests/directives.t.js 740
tests/filters.t.js 1450
tests/greetings.t.js 230
tests/hash.t.js 1190
tests/hello.t.js 430
tests/jemplate2/greeting/english 01
tests/jemplate2/greeting/spanish 01
tests/jemplate2/greetings 03
tests/jemplate2/hello 05
tests/join.t.js 190
tests/jquery-1.2.3.js 34080
tests/jquery.js 34080
tests/js/Test/Jemplate.js 029
tests/js/jemplate_dummy_plugin.js 018
tests/list.t.js 530
tests/localise.t.js 490
tests/loop.t.js 830
tests/mangler/README 120
tests/mangler/ctx.tst 110
tests/mangler/mangle.pl 1660
tests/mangler/test-data.tmpl.tt 220
tests/objects.t.js 690
tests/old/Makefile 016
tests/old/mangler/README 012
tests/old/mangler/ctx.tst 011
tests/old/mangler/mangle.pl 0166
tests/old/mangler/test-data.tmpl.tt 022
tests/plugins.t.js 340
tests/run/JemplateRuntime.js 8580
tests/run/Makefile 360
tests/run/basics.t.html 250
tests/run/bin/daemon 1080
tests/run/bin/pull-jemplates 250
tests/run/bin/render-template 320
tests/run/bugs.t.html 250
tests/run/default.t.html 250
tests/run/directives.t.html 250
tests/run/filters.t.html 250
tests/run/greetings.t.html 250
tests/run/hash.t.html 250
tests/run/hello.t.html 250
tests/run/index.html 320
tests/run/jemplate/basic_array1.html 100
tests/run/jemplate/default.html 30
tests/run/jemplate/directives1.html 70
tests/run/jemplate/directives2.html 70
tests/run/jemplate/directives3.html 50
tests/run/jemplate/directives4.html 60
tests/run/jemplate/directives5.html 40
tests/run/jemplate/filters_html.html 70
tests/run/jemplate/filters_indent.html 300
tests/run/jemplate/filters_null.html 10
tests/run/jemplate/filters_repeat.html 10
tests/run/jemplate/filters_replace.html 60
tests/run/jemplate/filters_truncate.html 70
tests/run/jemplate/filters_uri.html 50
tests/run/jemplate/hash_each.html 110
tests/run/jemplate/hash_exists.html 30
tests/run/jemplate/hash_import.html 60
tests/run/jemplate/hash_keys.html 20
tests/run/jemplate/hash_list.html 160
tests/run/jemplate/hash_nsort.html 20
tests/run/jemplate/hash_size.html 20
tests/run/jemplate/hash_sort.html 20
tests/run/jemplate/hash_values.html 20
tests/run/jemplate/hello.html 10
tests/run/jemplate/join.html 20
tests/run/jemplate/list.html 180
tests/run/jemplate/list_of_hash.html 40
tests/run/jemplate/localise1.html 80
tests/run/jemplate/localise2.html 80
tests/run/jemplate/loop1.html 30
tests/run/jemplate/loop2.html 90
tests/run/jemplate/loop3.html 30
tests/run/jemplate/loop4.html 30
tests/run/jemplate/loop5.html 60
tests/run/jemplate/myhashname.html 40
tests/run/jemplate/myobjectnameac.html 50
tests/run/jemplate/myobjectnameac2.html 40
tests/run/jemplate/myobjectnameattr.html 40
tests/run/jemplate/operator1.html 30
tests/run/jemplate/operator2.html 40
tests/run/jemplate/operator3.html 20
tests/run/jemplate/plugins_basic.html 90
tests/run/jemplate/stash-functions1.html 20
tests/run/jemplate/stash-functions2.html 20
tests/run/jemplate/stash-functions3.html 30
tests/run/jemplate/string_chunk.html 110
tests/run/jemplate/string_defined.html 20
tests/run/jemplate/string_hash.html 20
tests/run/jemplate/string_length.html 40
tests/run/jemplate/string_list.html 20
tests/run/jemplate/string_match.html 20
tests/run/jemplate/string_repeat.html 30
tests/run/jemplate/string_replace.html 30
tests/run/jemplate/string_search.html 30
tests/run/jemplate/string_size.html 20
tests/run/jemplate/string_split.html 40
tests/run/jemplate/test.html 40
tests/run/jemplate.js 21010
tests/run/jemplate2/greeting/english 10
tests/run/jemplate2/greeting/spanish 10
tests/run/jemplate2/greetings 30
tests/run/jemplate2/hello 10
tests/run/jemplate2.js 890
tests/run/jemplate_dummy_plugin.js 180
tests/run/join.t.html 250
tests/run/lib/Subclass.js 900
tests/run/lib/Test/Base.js 5950
tests/run/lib/Test/Builder.js 8410
tests/run/lib/Test/Harness/Browser.js 3630
tests/run/lib/Test/Harness.js 2770
tests/run/list.t.html 250
tests/run/localise.t.html 250
tests/run/loop.t.html 250
tests/run/objects.t.html 250
tests/run/plugins.t.html 250
tests/run/set.t.html 250
tests/run/stash.t.html 250
tests/run/string.t.html 250
tests/run/template/index.html 160
tests/run/template/test.html 240
tests/set.t.js 230
tests/stash.t.js 520
tests/string.t.js 1320
tests/t/basics.t.js 076
tests/t/bugs.t.js 056
tests/t/default.t.js 024
tests/t/directives.t.js 074
tests/t/filters.t.js 0167
tests/t/greetings.t.js 023
tests/t/hash.t.js 0119
tests/t/hello.t.js 044
tests/t/join.t.js 019
tests/t/list.t.js 053
tests/t/localise.t.js 049
tests/t/loop.t.js 083
tests/t/objects.t.js 069
tests/t/plugins.t.js 034
tests/t/set.t.js 023
tests/t/stash.t.js 0208
tests/t/string.t.js 0132
tests/t/test.html 016
tests/template/index.html 016
tests/template/test.html 016
tests/tjs/Makefile 088
tests/tjs/config.yaml 09
tjs/Makefile 660
tjs/bin/daemon 1250
tjs/bin/prepare-var-jt 250
tjs/bin/render-template 410
tjs/bin/test-run 290
tjs/js/Subclass.js 900
tjs/js/Test/Base.js 5950
tjs/js/Test/Builder.js 8410
tjs/js/Test/Harness/Browser.js 3630
tjs/js/Test/Harness.js 2770
tjs/js/Test/Jemplate.js 370
tjs/js/jemplate_dummy_plugin.js 180
tjs/js/jquery-1.2.3.js 34080
tjs/js/jquery.js 34080
tjs/jt-greeting/greeting/english 10
tjs/jt-greeting/greeting/spanish 10
tjs/jt-greeting/greetings 30
tjs/jt-greeting/hello 50
tjs/t/basics.t.js 760
tjs/t/bugs.t.js 560
tjs/t/default.t.js 240
tjs/t/directives.t.js 740
tjs/t/filters.t.js 1670
tjs/t/greetings.t.js 230
tjs/t/hash.t.js 1190
tjs/t/hello.t.js 430
tjs/t/join.t.js 190
tjs/t/list.t.js 530
tjs/t/localise.t.js 490
tjs/t/loop.t.js 830
tjs/t/objects.t.js 690
tjs/t/plugins.t.js 340
tjs/t/set.t.js 230
tjs/t/stash.t.js 520
tjs/t/string.t.js 1320
tjs/tt/index.html 160
tjs/tt/test.html 240
243 files changed (This is a version diff) 3495415305
@@ -1,4 +1,90 @@
 ---
+version: 0.261
+date: Tuesday April 27 09:35:06 PDT 2010
+changes:
+- Fixed rt56956 with test case - DIV operator broken (Agent Zhang)
+
+---
+version: 0.260
+date: Monday April 26 12:03:40 PDT 2010
+
+---
+version: 0.25_2
+date: Saturday April 24 18:48:53 PDT 2010
+changes:
+- Implemented rt4308 - Capture implementation (Tom Molesworth)
+- Added test for rt37570 - List method for lists (fixed in 5eaf340da...)
+
+---
+version: 0.25_1
+date: Thursday April 22 17:58:53 PDT 2010
+changes:
+- Fixed rt53454 with test case - Support rudimentary array range operator
+- Fixed rt53453 with test case - Make Jemplate slice like TT slice
+- Fixed rt53967 with test case - Compiler double-dot path fail
+- Added test case for rt37539 - Intermediate instantiation
+- Added test case for rt37540 - Iterate over empty object
+- Added test case for rt37895 - Inconsistent jemplate version
+
+---
+version: 0.25
+date:    Wednesday April 21 13:44:19 PDT 2010
+changes:
+- release
+
+---
+version: 0.24_4
+date:    Saturday January 09 17:06:40 PST 2010
+changes:
+- added .pairs (VMethod) functionality with tests
+- fixed quoted.t failing test
+
+---
+version: 0.24_3
+date:    Mon Jun 8 2009
+changes:
+- fixed bug, related to empty template body (http://rt.cpan.org/Public/Bug/Display.html?id=37061)
+- fixed bug in Parser.yp, which produced compilation error for macroses
+- fixed "intermediate instantiation" issue (http://rt.cpan.org/Public/Bug/Display.html?id=37539)
+- fixed "iteration over empty object" issue (http://rt.cpan.org/Public/Bug/Display.html?id=37540)
+- added and tested MACRO directive
+- added PRE_PROCESS and POST_PROCESS configuration options
+- added and tested global scope access via GLOBAL modifier (GLOBAL.jQeuery etc)
+- added dummy LOCAL modifier, for symmetry
+- added "init" method for Jemplate singleton
+- added experimental RAW directive (RAW jQuery, equivalent of jQuery = GLOBAL.jQuery)
+
+VMethods:
+- added "list" for lists (http://rt.cpan.org/Public/Bug/Display.html?id=37570)
+- added "item" for hashes
+- fixed "import" for hashes
+
+Test suite:
+- combined back to a single copy
+
+Patches:
+- Integrated patch from RT#37895
+
+version: 0.24_2
+date: Monday June 08 10:37:54 PDT 2009
+changes:
+- Fix to get tests working on MSWin32 (rt23883)
+
+---
+version: 0.24
+date:    Fri Sep 19 15:09:35 PDT 2008
+changes:
+- Convert tests to standard Test.Base setup!
+
+---
+version: 0.23_1
+date: Monday August 18 15:22:03 PDT 2008
+changes:
+- Fixed issue with runtime_source_code that broke Catalyst::View::Jemplate
+- Some beta MACRO functionality
+- Ingy refactored tjs/ into tests/
+
+---
 version: 0.23
 date: Friday June 06 12:51:18 PDT 2008
 changes:
@@ -112,7 +112,7 @@ completion, the second is for the testing of the feature.
     ++ [% WRAPPER template [var = value ...] %] text... [% END %] 
     ++ [% BLOCK [name] %] content... [% END %] 
     ++ [% FILTER filter %] text... [% END %] 
-    -- [% MACRO name[(varlist)] directive %] 
+    +- [% MACRO name[(varlist)] directive %] 
     -- [% USE plugin[(param, ...)] %] 
     xx [% PERL %] code... [% END %] 
     xx [% RAWPERL %] code... [% END %] 
@@ -158,8 +158,9 @@ completion, the second is for the testing of the feature.
 
     ++ each            list of alternating keys/values 
     ++ exists(key)     does key exist? 
-    -- import(hash2)   import contents of hash2 
-    -- import          import into current namespace hash 
+    +- import(hash2)   import contents of hash2 
+    +- import          import into current namespace hash 
+    +- item            retrieve value using string
     ++ keys            list of keys 
     ++ list            returns alternating key, value 
     ++ nsort           keys sorted numerically 
@@ -275,8 +276,8 @@ Refer to documentation for details of individual plugins.
 === Runtime processing options 
 
     -- EVAL_PERL       process PERL/RAWPERL blocks (0) 
-    -- PRE_PROCESS     template(s) to process before main template 
-    -- POST_PROCESS    template(s) to process after main template 
+    ++ PRE_PROCESS     template(s) to process before main template 
+    +- POST_PROCESS    template(s) to process after main template 
     -- PROCESS         template(s) to process instead of main template 
     -- ERROR           name of error template or reference to hash 
                        array mapping error types to templates 
@@ -1,6 +1,7 @@
 bin/jemplate
 bin/README
 Changes
+ToDo
 DESIGN
 doc/html/Jemplate.html
 doc/Makefile
@@ -29,10 +30,10 @@ inc/Module/Install/Fetch.pm
 inc/Module/Install/Include.pm
 inc/Module/Install/Makefile.pm
 inc/Module/Install/Metadata.pm
-inc/Module/Install/Scripts.pm
 inc/Module/Install/TestBase.pm
 inc/Module/Install/Win32.pm
 inc/Module/Install/WriteAll.pm
+inc/Module/Install/Scripts.pm
 inc/Spiffy.pm
 inc/Test/Base.pm
 inc/Test/Base/Filter.pm
@@ -53,6 +54,9 @@ META.yml
 README
 t/a.js
 t/assets/jt/hello
+t/assets/jt/a/.hidden/apple
+t/assets/jt/a/b/c/.hidden
+t/assets/jt/a/b/cherry
 t/b.js
 t/block.t
 t/call.t
@@ -72,6 +76,7 @@ t/list/foo/two
 t/list/one
 t/list/three
 t/list/two
+t/macro.t
 t/process.t
 t/quoted.t
 t/runtime.t
@@ -83,161 +88,60 @@ t/TestJemplate.pm
 t/throw.t
 t/while.t
 t/wrapper.t
-tests/basics.t.js
-tests/bugs.t.js
-tests/config.yaml
-tests/default.t.js
-tests/directives.t.js
-tests/filters.t.js
-tests/greetings.t.js
-tests/hash.t.js
-tests/hello.t.js
-tests/join.t.js
-tests/jquery-1.2.3.js
-tests/jquery.js
-tests/list.t.js
-tests/localise.t.js
-tests/loop.t.js
+t/pairs.t
+t/9bug/37539-intermediate-instantiation.t
+t/9bug/37540-iterate-over-empty-object.t
+t/9bug/37895-inconsistent-jemplate-version.t
+t/9bug/53967-compiler-double-dot-path-fail.t
+t/9bug/53453-make-Jemplate-slice-like-TT-slice.t
+t/9bug/53454-array-range-operator.t
+t/9bug/37570-list-for-lists.t
+t/9bug/43809-capture-implementation.t
+t/9bug/56965-DIV-operator-broken.t
 tests/Makefile
-tests/mangler/ctx.tst
-tests/mangler/mangle.pl
-tests/mangler/README
-tests/mangler/test-data.tmpl.tt
-tests/objects.t.js
-tests/plugins.t.js
-tests/run/basics.t.html
-tests/run/bin/daemon
-tests/run/bin/pull-jemplates
-tests/run/bin/render-template
-tests/run/bugs.t.html
-tests/run/default.t.html
-tests/run/directives.t.html
-tests/run/filters.t.html
-tests/run/greetings.t.html
-tests/run/hash.t.html
-tests/run/hello.t.html
-tests/run/index.html
-tests/run/jemplate.js
-tests/run/jemplate/basic_array1.html
-tests/run/jemplate/default.html
-tests/run/jemplate/directives1.html
-tests/run/jemplate/directives2.html
-tests/run/jemplate/directives3.html
-tests/run/jemplate/directives4.html
-tests/run/jemplate/directives5.html
-tests/run/jemplate/filters_html.html
-tests/run/jemplate/filters_indent.html
-tests/run/jemplate/filters_null.html
-tests/run/jemplate/filters_repeat.html
-tests/run/jemplate/filters_replace.html
-tests/run/jemplate/filters_truncate.html
-tests/run/jemplate/filters_uri.html
-tests/run/jemplate/hash_each.html
-tests/run/jemplate/hash_exists.html
-tests/run/jemplate/hash_import.html
-tests/run/jemplate/hash_keys.html
-tests/run/jemplate/hash_list.html
-tests/run/jemplate/hash_nsort.html
-tests/run/jemplate/hash_size.html
-tests/run/jemplate/hash_sort.html
-tests/run/jemplate/hash_values.html
-tests/run/jemplate/hello.html
-tests/run/jemplate/join.html
-tests/run/jemplate/list.html
-tests/run/jemplate/list_of_hash.html
-tests/run/jemplate/localise1.html
-tests/run/jemplate/localise2.html
-tests/run/jemplate/loop1.html
-tests/run/jemplate/loop2.html
-tests/run/jemplate/loop3.html
-tests/run/jemplate/loop4.html
-tests/run/jemplate/loop5.html
-tests/run/jemplate/myhashname.html
-tests/run/jemplate/myobjectnameac.html
-tests/run/jemplate/myobjectnameac2.html
-tests/run/jemplate/myobjectnameattr.html
-tests/run/jemplate/operator1.html
-tests/run/jemplate/operator2.html
-tests/run/jemplate/operator3.html
-tests/run/jemplate/plugins_basic.html
-tests/run/jemplate/stash-functions1.html
-tests/run/jemplate/stash-functions2.html
-tests/run/jemplate/stash-functions3.html
-tests/run/jemplate/string_chunk.html
-tests/run/jemplate/string_defined.html
-tests/run/jemplate/string_hash.html
-tests/run/jemplate/string_length.html
-tests/run/jemplate/string_list.html
-tests/run/jemplate/string_match.html
-tests/run/jemplate/string_repeat.html
-tests/run/jemplate/string_replace.html
-tests/run/jemplate/string_search.html
-tests/run/jemplate/string_size.html
-tests/run/jemplate/string_split.html
-tests/run/jemplate/test.html
-tests/run/jemplate2.js
-tests/run/jemplate2/greeting/english
-tests/run/jemplate2/greeting/spanish
-tests/run/jemplate2/greetings
-tests/run/jemplate2/hello
-tests/run/jemplate_dummy_plugin.js
-tests/run/JemplateRuntime.js
-tests/run/join.t.html
-tests/run/lib/Subclass.js
-tests/run/lib/Test/Base.js
-tests/run/lib/Test/Builder.js
-tests/run/lib/Test/Harness.js
-tests/run/lib/Test/Harness/Browser.js
-tests/run/list.t.html
-tests/run/localise.t.html
-tests/run/loop.t.html
-tests/run/Makefile
-tests/run/objects.t.html
-tests/run/plugins.t.html
-tests/run/set.t.html
-tests/run/stash.t.html
-tests/run/string.t.html
-tests/run/template/index.html
-tests/run/template/test.html
-tests/set.t.js
-tests/stash.t.js
-tests/string.t.js
-tests/Test/Jemplate.js
-tjs/bin/daemon
-tjs/bin/prepare-var-jt
-tjs/bin/render-template
-tjs/bin/test-run
-tjs/js/jemplate_dummy_plugin.js
-tjs/js/jquery-1.2.3.js
-tjs/js/jquery.js
-tjs/js/Subclass.js
-tjs/js/Test/Base.js
-tjs/js/Test/Builder.js
-tjs/js/Test/Harness.js
-tjs/js/Test/Harness/Browser.js
-tjs/js/Test/Jemplate.js
-tjs/jt-greeting/greeting/english
-tjs/jt-greeting/greeting/spanish
-tjs/jt-greeting/greetings
-tjs/jt-greeting/hello
-tjs/Makefile
-tjs/t/basics.t.js
-tjs/t/bugs.t.js
-tjs/t/default.t.js
-tjs/t/directives.t.js
-tjs/t/filters.t.js
-tjs/t/greetings.t.js
-tjs/t/hash.t.js
-tjs/t/hello.t.js
-tjs/t/join.t.js
-tjs/t/list.t.js
-tjs/t/localise.t.js
-tjs/t/loop.t.js
-tjs/t/objects.t.js
-tjs/t/plugins.t.js
-tjs/t/set.t.js
-tjs/t/stash.t.js
-tjs/t/string.t.js
-tjs/tt/index.html
-tjs/tt/test.html
-ToDo
+tests/README
+tests/bin/daemon
+tests/bin/prepare-var-jt
+tests/bin/pull-jemplates
+tests/bin/render-template
+tests/bin/test-run
+tests/config.yaml
+tests/jemplate2/greeting/english
+tests/jemplate2/greeting/spanish
+tests/jemplate2/greetings
+tests/jemplate2/hello
+tests/js/Subclass.js
+tests/js/Test/Base.js
+tests/js/Test/Builder.js
+tests/js/Test/Harness/Browser.js
+tests/js/Test/Harness.js
+tests/js/Test/Jemplate.js
+tests/js/jemplate_dummy_plugin.js
+tests/js/jquery-1.2.3.js
+tests/old/Makefile
+tests/old/mangler/README
+tests/old/mangler/ctx.tst
+tests/old/mangler/mangle.pl
+tests/old/mangler/test-data.tmpl.tt
+tests/t/basics.t.js
+tests/t/bugs.t.js
+tests/t/default.t.js
+tests/t/directives.t.js
+tests/t/filters.t.js
+tests/t/greetings.t.js
+tests/t/hash.t.js
+tests/t/hello.t.js
+tests/t/join.t.js
+tests/t/list.t.js
+tests/t/localise.t.js
+tests/t/loop.t.js
+tests/t/objects.t.js
+tests/t/plugins.t.js
+tests/t/set.t.js
+tests/t/stash.t.js
+tests/t/string.t.js
+tests/t/test.html
+tests/template/index.html
+tests/template/test.html
+tests/tjs/Makefile
+tests/tjs/config.yaml
@@ -2,12 +2,16 @@
 abstract: 'JavaScript Templating with Template Toolkit'
 author:
   - 'Ingy döt Net <ingy@cpan.org>'
+build_requires:
+  ExtUtils::MakeMaker: 6.42
+configure_requires:
+  ExtUtils::MakeMaker: 6.42
 distribution_type: module
-generated_by: 'Module::Install version 0.710'
+generated_by: 'Module::Install version 0.91'
 license: perl
 meta-spec:
-  url: http://module-build.sourceforge.net/META-spec-v1.3.html
-  version: 1.3
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
 name: Jemplate
 no_index:
   directory:
@@ -16,6 +20,9 @@ no_index:
     - t
 requires:
   File::Find::Rule: 0.30
+  Filter::Util::Call: 0
   Template: 2.19
   perl: 5.6.1
-version: 0.23
+resources:
+  license: http://dev.perl.org/licenses/
+version: 0.261
@@ -3,13 +3,33 @@ use inc::Module::Install;
 name            'Jemplate';
 all_from        'lib/Jemplate.pm';
 
+{
+    require ExtUtils::MakeMaker;
+    use strict;
+    no strict 'refs';
+
+    my $libscan = \&{"ExtUtils::MM_Any::libscan"};
+    *{"ExtUtils::MM_Any::libscan"} = sub {
+        return '' unless $libscan->(@_);
+        return '' if $_[1] =~ /\.sw[p-z]$/;
+        return $_[1];
+    };
+}
+
 #build_requires        'Directory::Scratch';
 
+if (-e 'inc/.author') {
+    my $all_from = join '/', 'lib', split m/-/, name . '.pm';
+    `perldoc -tF $all_from > README` if ! -e 'README' || (stat $all_from)[9] > (stat 'README')[9];
+}
+
 requires        'Template' => '2.19';
 requires        'File::Find::Rule' => '0.30';
 
 use_test_base;
 
+tests_recursive;
+
 no_index        directory => 'examples';
 
 clean_files     't/Jemplate.js';
@@ -1,3 +1,6 @@
+VERSION
+    Version 0.261
+
 NAME
     Jemplate - JavaScript Templating with Template Toolkit
 
@@ -22,6 +25,10 @@ SYNOPSIS
             Jemplate.process('my-template.html', data, '#some-div');
         });
 
+    From the commandline:
+
+        jemplate --runtime --compile path/to/jemplate/directory/ > jemplate.js
+
 DESCRIPTION
     Jemplate is a templating framework for JavaScript that is built over
     Perl's Template Toolkit (TT2).
@@ -161,12 +168,13 @@ CURRENT SUPPORT
       * [% LAST %]
       * [% CLEAR %]
       * [%# this is a comment %]
+      * [% MACRO name(param1, param2) BLOCK %] ... [% END %]
 
     ALL of the string virtual functions are supported.
 
     ALL of the array virtual functions are supported:
 
-    ALL of the hash virtual functions are supported (except for import):
+    ALL of the hash virtual functions are supported:
 
     MANY of the standard filters are implemented.
 
@@ -185,8 +193,8 @@ BROWSER SUPPORT
     All tests run 100% successful in the above browsers.
 
 DEVELOPMENT
-    The bleeding edge code is available via Subversion at
-    http://svn.jemplate.net/repo/trunk/
+    The bleeding edge code is available via Git at
+    git://github.com/ingydotnet/jemplate.git
 
     You can run the runtime tests directly from
     http://svn.jemplate.net/repo/trunk/tests/run/index.html or from the
@@ -229,6 +237,8 @@ AUTHORS
 
     Robert Krimen <robertkrimen@gmail.com>
 
+    Nickolay Platonov <nickolay8@gmail.com>
+
 COPYRIGHT
     Copyright (c) 2006-2008. Ingy döt Net.
 
@@ -1,3 +1,7 @@
+Add note about commit 3c4d9 referring to wrong rt, should refer to 23883
+
+TODO: Does use_test_base not work? make test compains that Test::Base is not installed -rokr
+    
 FUTURE:
 
 09:55 < nkuttler_> apparently firefox sets the accept header to request xml rather than json, which obviously is incompatible with later JSON.parse calls. I have added a 
@@ -182,15 +182,13 @@ BEGIN {
 
 package File::Find::Rule;
 use strict;
-use vars qw/$VERSION $AUTOLOAD/;
 use File::Spec;
 use Text::Glob 'glob_to_regex';
 use Number::Compare;
 use Carp qw/croak/;
 use File::Find (); # we're only wrapping for now
-use Cwd;           # 5.00503s File::Find goes screwy with max_depth == 0
 
-$VERSION = '0.30';
+our $VERSION = '0.32';
 
 sub import {
     my $pkg = shift;
@@ -245,8 +243,8 @@ sub new {
     my $referent = shift;
     my $class = ref $referent || $referent;
     bless {
-        rules    => [],  # [0]
-        subs     => [],  # [1]
+        rules    => [],
+        subs     => {},
         iterator => [],
         extras   => {},
         maxdepth => undef,
@@ -349,15 +347,15 @@ use vars qw( @stat_tests );
 
 sub any {
     my $self = _force_object shift;
-    my @rulesets = @_;
-
+    # compile all the subrules to code fragments
     push @{ $self->{rules} }, {
-        rule => 'any',
-        code => '(' . join( ' || ', map {
-            "( " . $_->_compile( $self->{subs} ) . " )"
-        } @_ ) . ")",
+        rule => "any",
+        code => '(' . join( ' || ', map '( ' . $_->_compile . ' )', @_ ). ')',
         args => \@_,
     };
+    
+    # merge all the subs hashes of the kids into ourself
+    %{ $self->{subs} } = map { %{ $_->{subs} } } $self, @_;
     $self;
 }
 
@@ -366,15 +364,15 @@ sub any {
 
 sub not {
     my $self = _force_object shift;
-    my @rulesets = @_;
 
     push @{ $self->{rules} }, {
         rule => 'not',
-        args => \@rulesets,
-        code => '(' . join ( ' && ', map {
-            "!(". $_->_compile( $self->{subs} ) . ")"
-        } @_ ) . ")",
+        args => \@_,
+        code => '(' . join ( ' && ', map { "!(". $_->_compile . ")" } @_ ) . ")",
     };
+    
+    # merge all the subs hashes into us
+    %{ $self->{subs} } = map { %{ $_->{subs} } } $self, @_;
     $self;
 }
 
@@ -465,6 +463,7 @@ sub relative () {
 
 sub DESTROY {}
 sub AUTOLOAD {
+    our $AUTOLOAD;
     $AUTOLOAD =~ /::not_([^:]*)$/
       or croak "Can't locate method $AUTOLOAD";
     my $method = $1;
@@ -485,8 +484,8 @@ sub in {
     my $self = _force_object shift;
 
     my @found;
-    my $fragment = $self->_compile( $self->{subs} );
-    my @subs = @{ $self->{subs} };
+    my $fragment = $self->_compile;
+    my %subs = %{ $self->{subs} };
 
     warn "relative mode handed multiple paths - that's a bit silly\n"
       if $self->{relative} && @_ > 1;
@@ -526,11 +525,10 @@ sub in {
     }';
 
     #use Data::Dumper;
-    #print Dumper \@subs;
+    #print Dumper \%subs;
     #warn "Compiled sub: '$code'\n";
 
     my $sub = eval "$code" or die "compile error '$code' $@";
-    my $cwd = getcwd;
     for my $path (@_) {
         # $topdir is used for relative and maxdepth
         $topdir = $path;
@@ -540,7 +538,6 @@ sub in {
           unless $topdir eq '/';
         $self->_call_find( { %{ $self->{extras} }, wanted => $sub }, $path );
     }
-    chdir $cwd;
 
     return @found;
 }
@@ -552,19 +549,20 @@ sub _call_find {
 
 sub _compile {
     my $self = shift;
-    my $subs = shift; # [1]
 
     return '1' unless @{ $self->{rules} };
     my $code = join " && ", map {
         if (ref $_->{code}) {
-            push @$subs, $_->{code};
-            "\$subs[$#{$subs}]->(\@args) # $_->{rule}\n";
+            my $key = "$_->{code}";
+            $self->{subs}{$key} = $_->{code};
+            "\$subs{'$key'}->(\@args) # $_->{rule}\n";
         }
         else {
             "( $_->{code} ) # $_->{rule}\n";
         }
     } @{ $self->{rules} };
 
+    #warn $code;
     return $code;
 }
 
@@ -597,15 +595,14 @@ BEGIN {
 package Template::Constants;
 
 require Exporter;
-
 use strict;
 use warnings;
-use base 'Exporter';
-
+use Exporter;
 use vars qw( @EXPORT_OK %EXPORT_TAGS );
-use vars qw( $DEBUG_OPTIONS @STATUS @ERROR @CHOMP @DEBUG);
+use vars qw( $DEBUG_OPTIONS @STATUS @ERROR @CHOMP @DEBUG @ISA );
+@ISA = qw( Exporter );
 
-our $VERSION = 2.74;
+our $VERSION = 2.75;
 
 
 
@@ -739,7 +736,7 @@ use strict;
 use warnings;
 use Template::Constants;
 
-our $VERSION = 2.77;
+our $VERSION = 2.78;
 
 
 
@@ -747,7 +744,8 @@ sub new {
     my $class = shift;
     my ($argnames, @args, $arg, $cfg);
 
-    {	no strict qw( refs );
+    {   no strict 'refs';
+        no warnings 'once';
         $argnames = \@{"$class\::BASEARGS"} || [ ];
     }
 
@@ -759,10 +757,10 @@ sub new {
     }
 
     # fold all remaining args into a hash, or use provided hash ref
-    $cfg  = defined $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
+    $cfg  = defined $_[0] && ref($_[0]) eq 'HASH' ? shift : { @_ };
 
     my $self = bless {
-        map { ($_ => shift @args) } @$argnames,
+        (map { ($_ => shift @args) } @$argnames),
         _ERROR  => '',
         DEBUG   => 0,
     }, $class;
@@ -797,11 +795,6 @@ sub _init {
 }
 
 
-sub DEBUG {
-    my $self = shift;
-    print STDERR "DEBUG: ", @_;
-}
-
 sub debug {
     my $self = shift;
     my $msg  = join('', @_);
@@ -847,7 +840,7 @@ use vars qw( $VERSION $DEBUG $ERROR $INSTDIR
              $LATEX_PATH $PDFLATEX_PATH $DVIPS_PATH
              $STASH $SERVICE $CONTEXT $CONSTANTS @PRELOAD );
 
-$VERSION   = 2.74;
+$VERSION   = 2.75;
 $DEBUG     = 0 unless defined $DEBUG;
 $ERROR     = '';
 $CONTEXT   = 'Template::Context';
@@ -863,7 +856,7 @@ $CONSTANTS = 'Template::Namespace::Constants';
 @PRELOAD   = ( $CONTEXT, $FILTERS, $ITERATOR, $PARSER,
                $PLUGINS, $PROVIDER, $SERVICE, $STASH );
 
-$INSTDIR  = '/usr/local/tt2';
+$INSTDIR  = '';
 
 
 
@@ -883,9 +876,7 @@ sub load {
     my ($class, $module) = @_;
     $module =~ s[::][/]g;
     $module .= '.pm';
-    eval {
-	require $module;
-    };
+    eval { require $module; };
     return $@ ? $class->error("failed to load $module: $@") : 1;
 }
 
@@ -893,20 +884,20 @@ sub load {
 
 sub parser {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH'
+               ? shift : { @_ };
 
     return undef unless $class->load($PARSER);
     return $PARSER->new($params) 
-	|| $class->error("failed to create parser: ", $PARSER->error);
+        || $class->error("failed to create parser: ", $PARSER->error);
 }
 
 
 
 sub provider {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($PROVIDER);
     return $PROVIDER->new($params) 
@@ -918,26 +909,26 @@ sub provider {
 
 sub plugins {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($PLUGINS);
     return $PLUGINS->new($params)
-	|| $class->error("failed to create plugin provider: ",
-			 $PLUGINS->error);
+        || $class->error("failed to create plugin provider: ",
+                         $PLUGINS->error);
 }
 
 
 
 sub filters {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($FILTERS);
     return $FILTERS->new($params)
-	|| $class->error("failed to create filter provider: ",
-			 $FILTERS->error);
+        || $class->error("failed to create filter provider: ",
+                         $FILTERS->error);
 }
 
 
@@ -948,56 +939,56 @@ sub iterator {
 
     return undef unless $class->load($ITERATOR);
     return $ITERATOR->new($list, @_)
-	|| $class->error("failed to create iterator: ", $ITERATOR->error);
+        || $class->error("failed to create iterator: ", $ITERATOR->error);
 }
 
 
 
 sub stash {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($STASH);
     return $STASH->new($params) 
-	|| $class->error("failed to create stash: ", $STASH->error);
+        || $class->error("failed to create stash: ", $STASH->error);
 }
 
 
 
 sub context {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($CONTEXT);
     return $CONTEXT->new($params) 
-	|| $class->error("failed to create context: ", $CONTEXT->error);
+        || $class->error("failed to create context: ", $CONTEXT->error);
 }
 
 
 
 sub service {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($SERVICE);
     return $SERVICE->new($params) 
-	|| $class->error("failed to create context: ", $SERVICE->error);
+        || $class->error("failed to create context: ", $SERVICE->error);
 }
 
 
 
 sub constants {
     my $class  = shift;
-    my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') 
-	       ? shift : { @_ };
+    my $params = defined($_[0]) && ref($_[0]) eq 'HASH' 
+               ? shift : { @_ };
 
     return undef unless $class->load($CONSTANTS);
     return $CONSTANTS->new($params) 
-	|| $class->error("failed to create constants namespace: ", 
-			 $CONSTANTS->error);
+        || $class->error("failed to create constants namespace: ", 
+                         $CONSTANTS->error);
 }
 
 
@@ -1005,7 +996,7 @@ sub constants {
 sub instdir {
     my ($class, $dir) = @_;
     my $inst = $INSTDIR 
-	|| return $class->error("no installation directory");
+        || return $class->error("no installation directory");
     $inst =~ s[/$][]g;
     $inst .= "/$dir" if $dir;
     return $inst;
@@ -1143,7 +1134,7 @@ sub process {
 
     die $context->catch($@)
         if $@;
-	
+        
     return $output;
 }
 
@@ -1170,9 +1161,9 @@ sub _dump {
     $output .= "BLOCK: $self->{ _BLOCK }\nDEFBLOCKS:\n";
 
     if ($dblks = $self->{ _DEFBLOCKS }) {
-	foreach my $b (keys %$dblks) {
-	    $output .= "    $b: $dblks->{ $b }\n";
-	}
+        foreach my $b (keys %$dblks) {
+            $output .= "    $b: $dblks->{ $b }\n";
+        }
     }
 
     return $output;
@@ -1185,12 +1176,12 @@ sub as_perl {
     my ($class, $content) = @_;
     my ($block, $defblocks, $metadata) = @$content{ qw( BLOCK DEFBLOCKS METADATA ) };
 
-    $block =~ s/\n/\n    /g;
+    $block =~ s/\n(?!#line)/\n    /g;
     $block =~ s/\s+$//;
 
     $defblocks = join('', map {
         my $code = $defblocks->{ $_ };
-        $code =~ s/\n/\n        /g;
+        $code =~ s/\n(?!#line)/\n        /g;
         $code =~ s/\s*$//;
         "        '$_' => $code,\n";
     } keys %$defblocks);
@@ -1224,7 +1215,7 @@ sub write_perl_file {
     my ($fh, $tmpfile);
     
     return $class->error("invalid filename: $file")
-	unless $file =~ /^(.+)$/s;
+        unless $file =~ /^(.+)$/s;
 
     eval {
         require File::Temp;
@@ -1273,7 +1264,7 @@ use constant INFO  => 1;
 use constant TEXT  => 2;
 use overload q|""| => "as_string", fallback => 1;
 
-our $VERSION = 2.69;
+our $VERSION = 2.70;
 
 
 
@@ -1303,16 +1294,15 @@ sub text {
     my $textref = $self->[ TEXT ];
     
     if ($newtextref) {
-	$$newtextref .= $$textref if $textref && $textref ne $newtextref;
-	$self->[ TEXT ] = $newtextref;
-	return '';
-	
+        $$newtextref .= $$textref if $textref && $textref ne $newtextref;
+        $self->[ TEXT ] = $newtextref;
+        return '';
     }
     elsif ($textref) {
-	return $$textref;
+        return $$textref;
     }
     else {
-	return '';
+        return '';
     }
 }
 
@@ -1332,11 +1322,11 @@ sub select_handler {
     @hlut{ @options } = (1) x @options;
 
     while ($type) {
-	return $type if $hlut{ $type };
+        return $type if $hlut{ $type };
 
-	# strip .element from the end of the exception type to find a 
-	# more generic handler
-	$type =~ s/\.?[^\.]*$//;
+        # strip .element from the end of the exception type to find a 
+        # more generic handler
+        $type =~ s/\.?[^\.]*$//;
     }
     return undef;
 }
@@ -1360,6 +1350,9 @@ use base 'Template::Base';
 use Template::Config;
 use Template::Exception;
 use Template::Constants;
+use Scalar::Util 'blessed';
+
+use constant EXCEPTION => 'Template::Exception';
 
 our $VERSION = 2.80;
 our $DEBUG   = 0 unless defined $DEBUG;
@@ -1368,14 +1361,12 @@ our $ERROR   = '';
 
 
 
-
 sub process {
     my ($self, $template, $params) = @_;
     my $context = $self->{ CONTEXT };
     my ($name, $output, $procout, $error);
     $output = '';
 
-
     $self->debug("process($template, ", 
                  defined $params ? $params : '<no params>',
                  ')') if $self->{ DEBUG };
@@ -1392,6 +1383,7 @@ sub process {
     # localise the variable stash with any parameters passed
     # and set the 'template' variable
     $params ||= { };
+    # TODO: change this to C<||=> so we can use a template parameter
     $params->{ template } = $template 
         unless ref $template eq 'CODE';
     $context->localise($params);
@@ -1502,7 +1494,7 @@ sub _recover {
     # point... unless a module like CGI::Carp messes around with the 
     # DIE handler. 
     return undef
-        unless UNIVERSAL::isa($$error, 'Template::Exception');
+        unless blessed($$error) && $$error->isa(EXCEPTION);
 
     # a 'stop' exception is thrown by [% STOP %] - we return the output
     # buffer stored in the exception object
@@ -1604,7 +1596,7 @@ use constant LOAD   => 3;   # mtime of template
 use constant NEXT   => 4;   # link to next item in cache linked list
 use constant STAT   => 5;   # Time last stat()ed
 
-our $VERSION = 2.93;
+our $VERSION = 2.94;
 our $DEBUG   = 0 unless defined $DEBUG;
 our $ERROR   = '';
 
@@ -1713,7 +1705,7 @@ sub load {
               || return ($self->error(), Template::Constants::STATUS_ERROR);
 
           foreach my $dir (@$paths) {
-              $path = "$dir/$name";
+              $path = File::Spec->catfile($dir, $name);
               last INCPATH
                   if $self->_template_modified($path);
           }
@@ -1770,7 +1762,7 @@ sub paths {
             unshift(@ipaths, @$dpaths);
             next;
         }
-        elsif (UNIVERSAL::can($dir, 'paths')) {
+        elsif (ref($dir) && UNIVERSAL::can($dir, 'paths')) {
             $dpaths = $dir->paths()
                 || return $self->error($dir->error());
             unshift(@ipaths, @$dpaths);
@@ -1854,8 +1846,8 @@ sub _init {
             my $wdir = $dir;
             $wdir =~ s[:][]g if $^O eq 'MSWin32';
             $wdir =~ /(.*)/;  # untaint
-            $wdir = $1;
-            $wdir = File::Spec->catfile($cdir, $1);
+            $wdir = "$1";     # quotes work around bug in Strawberry Perl
+            $wdir = File::Spec->catfile($cdir, $wdir);
             File::Path::mkpath($wdir) unless -d $wdir;
         }
     }
@@ -1929,19 +1921,26 @@ sub _fetch {
         warn($self->error(), "\n");
     }
 
-    # Now fetch template from source, compile, and cache.
+    # load template from source
     my ($template, $error) = $self->_load($name, $t_name);
-    unless ($error) {
-        ($template, $error) = $self->_compile($template, $self->_compiled_filename($name) );
 
-        # Store compiled template and return it
-        return $self->store( $name, $template->{data} ) unless $error;
+    if ($error) {
+        # Template could not be fetched.  Add to the negative/notfound cache.
+        $self->{ NOTFOUND }->{ $name } = time;
+        return ( $template, $error );
     }
 
-    # Template could not be fetched.  Add to the negative/notfound cache.
-    $self->{ NOTFOUND }->{ $name } = time;
+    # compile template source
+    ($template, $error) = $self->_compile($template, $self->_compiled_filename($name) );
 
-    return ( $template, $error );
+    if ($error) {
+        # return any compile time error
+        return ($template, $error);
+    }
+    else {
+        # Store compiled template and return it
+        return $self->store($name, $template->{data}) ;
+    }
 }
 
 
@@ -2336,6 +2335,7 @@ sub _template_content {
     local *FH;
     if (open(FH, "< $path")) {
         local $/;
+        binmode(FH);
         $data = <FH>;
         $mod_date = (stat($path))[9];
         close(FH);
@@ -2430,6 +2430,7 @@ sub _dump_cache {
 sub _decode_unicode {
     my $self   = shift;
     my $string = shift;
+    return undef unless defined $string;
 
     use bytes;
     require Encode;
@@ -2473,6 +2474,7 @@ package Template;
 
 use strict;
 use warnings;
+use 5.006;
 use base 'Template::Base';
 
 use Template::Config;
@@ -2481,8 +2483,9 @@ use Template::Provider;
 use Template::Service;
 use File::Basename;
 use File::Path;
+use Scalar::Util qw(blessed);
 
-our $VERSION = '2.19';
+our $VERSION = '2.22';
 our $ERROR   = '';
 our $DEBUG   = 0;
 our $BINMODE = 0 unless defined $BINMODE;
@@ -2495,7 +2498,7 @@ Template::Config->preload() if $ENV{ MOD_PERL };
 sub process {
     my ($self, $template, $vars, $outstream, @opts) = @_;
     my ($output, $error);
-    my $options = (@opts == 1) && UNIVERSAL::isa($opts[0], 'HASH')
+    my $options = (@opts == 1) && ref($opts[0]) eq 'HASH'
         ? shift(@opts) : { @opts };
 
     $options->{ binmode } = $BINMODE
@@ -2512,7 +2515,7 @@ sub process {
         unless (ref $outstream) {
             my $outpath = $self->{ OUTPUT_PATH };
             $outstream = "$outpath/$outstream" if $outpath;
-        }	
+        }   
 
         # send processed template to output stream, checking for error
         return ($self->error($error))
@@ -2593,7 +2596,7 @@ sub _output {
     }
     # call the print() method on an object that implements the method
     # (e.g. IO::Handle, Apache::Request, etc)
-    elsif (UNIVERSAL::can($where, 'print')) {
+    elsif (blessed($where) && $where->can('print')) {
         $where->print($$textref);
     }
     # a simple string is taken as a filename
@@ -7954,18 +7957,14 @@ sub
 	[#Rule 39
 		 'atomdir', 1,
 sub
-{ $_[0]->{ INFOR } || $_[0]->{ INWHILE }
-                                        ? 'last LOOP;'
-                                        : 'last;'                         }
+{ $_[0]->block_label('last ', ';')    }
 	],
 	[#Rule 40
 		 'atomdir', 1,
 sub
-{ $_[0]->{ INFOR }
-                                        ? $factory->next()
-                                        : ($_[0]->{ INWHILE }
-                                           ? 'next LOOP;'
-                                           : 'next;')                     }
+{ $_[0]->in_block('FOR')
+                                        ? $factory->next($_[0]->block_label)
+                                        : $_[0]->block_label('next ', ';') }
 	],
 	[#Rule 41
 		 'atomdir', 2,
@@ -8050,13 +8049,12 @@ sub
 	[#Rule 56
 		 '@1-3', 0,
 sub
-{ $_[0]->{ INFOR }++                  }
+{ $_[0]->enter_block('FOR')           }
 	],
 	[#Rule 57
 		 'loop', 6,
 sub
-{ $_[0]->{ INFOR }--;
-                                      $factory->foreach(@{$_[2]}, $_[5])  }
+{ $factory->foreach(@{$_[2]}, $_[5], $_[0]->leave_block)  }
 	],
 	[#Rule 58
 		 'loop', 3,
@@ -8066,18 +8064,17 @@ sub
 	[#Rule 59
 		 '@2-3', 0,
 sub
-{ $_[0]->{ INWHILE }++                }
+{ $_[0]->enter_block('WHILE')         }
 	],
 	[#Rule 60
 		 'loop', 6,
 sub
-{ $_[0]->{ INWHILE }--;
-                                      $factory->while(@_[2, 5])           }
+{ $factory->while(@_[2, 5], $_[0]->leave_block) }
 	],
 	[#Rule 61
 		 'loop', 3,
 sub
-{ $factory->while(@_[3, 1])           }
+{ $factory->while(@_[3, 1]) }
 	],
 	[#Rule 62
 		 'loopvar', 4,
@@ -8650,17 +8647,6 @@ sub
 1;
 
 
-
-
-
-
-
-
-
-
-
-
-
 }
 #
 # Inline include of Template/Directive.pm
@@ -8711,14 +8697,14 @@ sub {
     my \$context = shift || die "template sub called without context\\n";
     my \$stash   = \$context->stash;
     my \$output  = '';
-    my \$error;
+    my \$_tt_error;
     
     eval { BLOCK: {
 $block
     } };
     if (\$@) {
-        \$error = \$context->catch(\$@, \\\$output);
-        die \$error unless \$error->type eq 'return';
+        \$_tt_error = \$context->catch(\$@, \\\$output);
+        die \$_tt_error unless \$_tt_error->type eq 'return';
     }
 
     return \$output;
@@ -8736,14 +8722,14 @@ sub anon_block {
 
 $OUTPUT do {
     my \$output  = '';
-    my \$error;
+    my \$_tt_error;
     
     eval { BLOCK: {
 $block
     } };
     if (\$@) {
-        \$error = \$context->catch(\$@, \\\$output);
-        die \$error unless \$error->type eq 'return';
+        \$_tt_error = \$context->catch(\$@, \\\$output);
+        die \$_tt_error unless \$_tt_error->type eq 'return';
     }
 
     \$output;
@@ -8795,13 +8781,13 @@ sub ident {
     # does the first element of the identifier have a NAMESPACE
     # handler defined?
     if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) {
-	my $key = $ident->[0];
-	$key =~ s/^'(.+)'$/$1/s;
-	if ($ns = $ns->{ $key }) {
-	    return $ns->ident($ident);
-	}
+        my $key = $ident->[0];
+        $key =~ s/^'(.+)'$/$1/s;
+        if ($ns = $ns->{ $key }) {
+            return $ns->ident($ident);
+        }
     }
-	
+        
     if (scalar @$ident <= 2 && ! $ident->[1]) {
         $ident = $ident->[0];
     }
@@ -8961,20 +8947,21 @@ sub if {
 
 
 sub foreach {
-    my ($class, $target, $list, $args, $block) = @_;
+    my ($class, $target, $list, $args, $block, $label) = @_;
     $args  = shift @$args;
     $args  = @$args ? ', { ' . join(', ', @$args) . ' }' : '';
+    $label ||= 'LOOP';
 
     my ($loop_save, $loop_set, $loop_restore, $setiter);
     if ($target) {
-        $loop_save    = 'eval { $oldloop = ' . &ident($class, ["'loop'"]) . ' }';
-        $loop_set     = "\$stash->{'$target'} = \$value";
-        $loop_restore = "\$stash->set('loop', \$oldloop)";
+        $loop_save    = 'eval { $_tt_oldloop = ' . &ident($class, ["'loop'"]) . ' }';
+        $loop_set     = "\$stash->{'$target'} = \$_tt_value";
+        $loop_restore = "\$stash->set('loop', \$_tt_oldloop)";
     }
     else {
         $loop_save    = '$stash = $context->localise()';
-        $loop_set     = "\$stash->get(['import', [\$value]]) "
-                        . "if ref \$value eq 'HASH'";
+        $loop_set     = "\$stash->get(['import', [\$_tt_value]]) "
+                        . "if ref \$_tt_value eq 'HASH'";
         $loop_restore = '$stash = $context->delocalise()';
     }
     $block = pad($block, 3) if $PRETTY;
@@ -8982,37 +8969,39 @@ sub foreach {
     return <<EOF;
 
 do {
-    my (\$value, \$error, \$oldloop);
-    my \$list = $list;
+    my (\$_tt_value, \$_tt_error, \$_tt_oldloop);
+    my \$_tt_list = $list;
     
-    unless (UNIVERSAL::isa(\$list, 'Template::Iterator')) {
-        \$list = Template::Config->iterator(\$list)
+    unless (UNIVERSAL::isa(\$_tt_list, 'Template::Iterator')) {
+        \$_tt_list = Template::Config->iterator(\$_tt_list)
             || die \$Template::Config::ERROR, "\\n"; 
     }
 
-    (\$value, \$error) = \$list->get_first();
+    (\$_tt_value, \$_tt_error) = \$_tt_list->get_first();
     $loop_save;
-    \$stash->set('loop', \$list);
+    \$stash->set('loop', \$_tt_list);
     eval {
-LOOP:   while (! \$error) {
+$label:   while (! \$_tt_error) {
             $loop_set;
 $block;
-            (\$value, \$error) = \$list->get_next();
+            (\$_tt_value, \$_tt_error) = \$_tt_list->get_next();
         }
     };
     $loop_restore;
     die \$@ if \$@;
-    \$error = 0 if \$error && \$error eq Template::Constants::STATUS_DONE;
-    die \$error if \$error;
+    \$_tt_error = 0 if \$_tt_error && \$_tt_error eq Template::Constants::STATUS_DONE;
+    die \$_tt_error if \$_tt_error;
 };
 EOF
 }
 
 
 sub next {
+    my ($class, $label) = @_;
+    $label ||= 'LOOP';
     return <<EOF;
-(\$value, \$error) = \$list->get_next();
-next LOOP;
+(\$_tt_value, \$_tt_error) = \$_tt_list->get_next();
+next $label;
 EOF
 }
 
@@ -9069,19 +9058,20 @@ EOF
 
 
 sub while {
-    my ($class, $expr, $block) = @_;
+    my ($class, $expr, $block, $label) = @_;
     $block = pad($block, 2) if $PRETTY;
+    $label ||= 'LOOP';
 
     return <<EOF;
 
 do {
-    my \$failsafe = $WHILE_MAX;
-LOOP:
-    while (--\$failsafe && ($expr)) {
+    my \$_tt_failsafe = $WHILE_MAX;
+$label:
+    while (--\$_tt_failsafe && ($expr)) {
 $block
     }
     die "WHILE loop terminated (> $WHILE_MAX iterations)\\n"
-        unless \$failsafe;
+        unless \$_tt_failsafe;
 };
 EOF
 }
@@ -9101,9 +9091,9 @@ sub switch {
         $block = $case->[1];
         $block = pad($block, 1) if $PRETTY;
         $caseblock .= <<EOF;
-\$match = $match;
-\$match = [ \$match ] unless ref \$match eq 'ARRAY';
-if (grep(/^\$result\$/, \@\$match)) {
+\$_tt_match = $match;
+\$_tt_match = [ \$_tt_match ] unless ref \$_tt_match eq 'ARRAY';
+if (grep(/^\\Q\$_tt_result\\E\$/, \@\$_tt_match)) {
 $block
     last SWITCH;
 }
@@ -9117,8 +9107,8 @@ EOF
 return <<EOF;
 
 do {
-    my \$result = $expr;
-    my \$match;
+    my \$_tt_result = $expr;
+    my \$_tt_match;
     SWITCH: {
 $caseblock
     }
@@ -9138,7 +9128,7 @@ sub try {
     $block = pad($block, 2) if $PRETTY;
     $final = pop @catch;
     $final = "# FINAL\n" . ($final ? "$final\n" : '')
-           . 'die $error if $error;' . "\n" . '$output;';
+           . 'die $_tt_error if $_tt_error;' . "\n" . '$output;';
     $final = pad($final, 1) if $PRETTY;
 
     $n = 0;
@@ -9151,14 +9141,14 @@ sub try {
         $mblock = pad($mblock, 1) if $PRETTY;
         push(@$handlers, "'$match'");
         $catchblock .= $n++ 
-            ? "elsif (\$handler eq '$match') {\n$mblock\n}\n" 
-               : "if (\$handler eq '$match') {\n$mblock\n}\n";
+            ? "elsif (\$_tt_handler eq '$match') {\n$mblock\n}\n" 
+               : "if (\$_tt_handler eq '$match') {\n$mblock\n}\n";
     }
-    $catchblock .= "\$error = 0;";
+    $catchblock .= "\$_tt_error = 0;";
     $catchblock = pad($catchblock, 3) if $PRETTY;
     if ($default) {
         $default = pad($default, 1) if $PRETTY;
-        $default = "else {\n    # DEFAULT\n$default\n    \$error = '';\n}";
+        $default = "else {\n    # DEFAULT\n$default\n    \$_tt_error = '';\n}";
     }
     else {
         $default = '# NO DEFAULT';
@@ -9170,16 +9160,16 @@ return <<EOF;
 
 $OUTPUT do {
     my \$output = '';
-    my (\$error, \$handler);
+    my (\$_tt_error, \$_tt_handler);
     eval {
 $block
     };
     if (\$@) {
-        \$error = \$context->catch(\$@, \\\$output);
-        die \$error if \$error->type =~ /^return|stop\$/;
-        \$stash->set('error', \$error);
-        \$stash->set('e', \$error);
-        if (defined (\$handler = \$error->select_handler($handlers))) {
+        \$_tt_error = \$context->catch(\$@, \\\$output);
+        die \$_tt_error if \$_tt_error->type =~ /^return|stop\$/;
+        \$stash->set('error', \$_tt_error);
+        \$stash->set('e', \$_tt_error);
+        if (defined (\$_tt_handler = \$_tt_error->select_handler($handlers))) {
 $catchblock
         }
 $default
@@ -9228,7 +9218,7 @@ sub clear {
 }
 
 
-sub break {
+sub OLD_break {
     return 'last LOOP;';
 }
 
@@ -9276,15 +9266,15 @@ sub view {
     return <<EOF;
 do {
     my \$output = '';
-    my \$oldv = \$stash->get('view');
-    my \$view = \$context->view($hash);
-    \$stash->set($name, \$view);
-    \$stash->set('view', \$view);
+    my \$_tt_oldv = \$stash->get('view');
+    my \$_tt_view = \$context->view($hash);
+    \$stash->set($name, \$_tt_view);
+    \$stash->set('view', \$_tt_view);
 
 $block
 
-    \$stash->set('view', \$oldv);
-    \$view->seal();
+    \$stash->set('view', \$_tt_oldv);
+    \$_tt_view->seal();
 };
 EOF
 }
@@ -9308,14 +9298,14 @@ $block
     local(\$Template::Perl::context) = \$context;
     local(\$Template::Perl::stash)   = \$stash;
 
-    my \$result = '';
-    tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$result;
-    my \$save_stdout = select *Template::Perl::PERLOUT;
+    my \$_tt_result = '';
+    tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$_tt_result;
+    my \$_tt_save_stdout = select *Template::Perl::PERLOUT;
 
     eval \$output;
-    select \$save_stdout;
+    select \$_tt_save_stdout;
     \$context->throw(\$@) if \$@;
-    \$result;
+    \$_tt_result;
 };
 EOF
 }
@@ -9360,12 +9350,12 @@ sub filter {
 
 $OUTPUT do {
     my \$output = '';
-    my \$filter = \$context->filter($name)
+    my \$_tt_filter = \$context->filter($name)
               || \$context->throw(\$context->error);
 
 $block
     
-    &\$filter(\$output);
+    &\$_tt_filter(\$output);
 };
 EOF
 }
@@ -9406,20 +9396,20 @@ sub macro {
         my $nargs = scalar @$args;
         $args = join(', ', map { "'$_'" } @$args);
         $args = $nargs > 1 
-            ? "\@args{ $args } = splice(\@_, 0, $nargs)"
-            : "\$args{ $args } = shift";
+            ? "\@_tt_args{ $args } = splice(\@_, 0, $nargs)"
+            : "\$_tt_args{ $args } = shift";
 
         return <<EOF;
 
 \$stash->set('$ident', sub {
     my \$output = '';
-    my (%args, \$params);
+    my (%_tt_args, \$_tt_params);
     $args;
-    \$params = shift;
-    \$params = { } unless ref(\$params) eq 'HASH';
-    \$params = { \%args, %\$params };
+    \$_tt_params = shift;
+    \$_tt_params = { } unless ref(\$_tt_params) eq 'HASH';
+    \$_tt_params = { \%_tt_args, %\$_tt_params };
 
-    my \$stash = \$context->localise(\$params);
+    my \$stash = \$context->localise(\$_tt_params);
     eval {
 $block
     };
@@ -9434,10 +9424,10 @@ EOF
         return <<EOF;
 
 \$stash->set('$ident', sub {
-    my \$params = \$_[0] if ref(\$_[0]) eq 'HASH';
+    my \$_tt_params = \$_[0] if ref(\$_[0]) eq 'HASH';
     my \$output = '';
 
-    my \$stash = \$context->localise(\$params);
+    my \$stash = \$context->localise(\$_tt_params);
     eval {
 $block
     };
@@ -9517,9 +9507,9 @@ our $DEFAULT_STYLE = {
 };
 
 our $QUOTED_ESCAPES = {
-	n => "\n",
-	r => "\r",
-	t => "\t",
+        n => "\n",
+        r => "\r",
+        t => "\t",
 };
 
 our $CHOMP_FLAGS  = qr/[-=~+]/;
@@ -9530,7 +9520,7 @@ our $CHOMP_FLAGS  = qr/[-=~+]/;
 
 sub new {
     my $class  = shift;
-    my $config = $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift(@_) : { @_ };
+    my $config = $_[0] && ref($_[0]) eq 'HASH' ? shift(@_) : { @_ };
     my ($tagstyle, $debug, $start, $end, $defaults, $grammar, $hash, $key, $udef);
 
     my $self = bless { 
@@ -9546,7 +9536,8 @@ sub new {
         FILE_INFO   => 1,
         GRAMMAR     => undef,
         _ERROR      => '',
-        FACTORY     => 'Template::Directive',
+        IN_BLOCK    => [ ],
+        FACTORY     => $config->{ FACTORY } || 'Template::Directive',
     }, $class;
 
     # update self with any relevant keys in config
@@ -9592,11 +9583,41 @@ sub new {
     
     $self->new_style($config)
         || return $class->error($self->error());
-	
+        
     return $self;
 }
 
 
+sub enter_block {
+    my ($self, $name) = @_;
+    my $blocks = $self->{ IN_BLOCK };
+    push(@{ $self->{ IN_BLOCK } }, $name);
+}
+
+sub leave_block {
+    my $self = shift;
+    my $label = $self->block_label;
+    pop(@{ $self->{ IN_BLOCK } });
+    return $label;
+}
+
+sub in_block {
+    my ($self, $name) = @_;
+    my $blocks = $self->{ IN_BLOCK };
+    return @$blocks && $blocks->[-1] eq $name;
+}
+
+sub block_label {
+    my ($self, $prefix, $suffix) = @_;
+    my $blocks = $self->{ IN_BLOCK };
+    my $name   = @$blocks 
+        ? $blocks->[-1] . scalar @$blocks 
+        : undef;
+    return join('', grep { defined $_ } $prefix, $name, $suffix);
+}
+
+
+
 
 sub new_style {
     my ($self, $config) = @_;
@@ -9640,7 +9661,7 @@ sub parse {
     my ($tokens, $block);
 
     $info->{ DEBUG } = $self->{ DEBUG_DIRS }
-	unless defined $info->{ DEBUG };
+        unless defined $info->{ DEBUG };
 
 
     # store for blocks defined in the template (see define_block())
@@ -9652,7 +9673,7 @@ sub parse {
 
     # split file into TEXT/DIRECTIVE chunks
     $tokens = $self->split_text($text)
-        || return undef;				    ## RETURN ##
+        || return undef;                                    ## RETURN ##
 
     push(@{ $self->{ FILEINFO } }, $info);
 
@@ -9661,7 +9682,7 @@ sub parse {
 
     pop(@{ $self->{ FILEINFO } });
 
-    return undef unless $block;				    ## RETURN ##
+    return undef unless $block;                             ## RETURN ##
 
     $self->debug("compiled main template document block:\n$block")
         if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
@@ -9682,12 +9703,13 @@ sub split_text {
     my $style = $self->{ STYLE }->[-1];
     my ($start, $end, $prechomp, $postchomp, $interp ) = 
         @$style{ qw( START_TAG END_TAG PRE_CHOMP POST_CHOMP INTERPOLATE ) };
+    my $tags_dir = $self->{ANYCASE} ? qr<TAGS>i : qr<TAGS>;
 
     my @tokens = ();
     my $line = 1;
 
-    return \@tokens					    ## RETURN ##
-	unless defined $text && length $text;
+    return \@tokens                                         ## RETURN ##
+        unless defined $text && length $text;
 
     # extract all directives from the text
     while ($text =~ s/
@@ -9720,7 +9742,7 @@ sub split_text {
                 if ($chomp && $pre) {
                     # chomp off whitespace and newline preceding directive
                     if ($chomp == CHOMP_ALL) { 
-                        $pre =~ s{ (\n|^) [^\S\n]* \z }{}mx;
+                        $pre =~ s{ (\r?\n|^) [^\S\n]* \z }{}mx;
                     }
                     elsif ($chomp == CHOMP_COLLAPSE) { 
                         $pre =~ s{ (\s+) \z }{ }x;
@@ -9763,7 +9785,7 @@ sub split_text {
         # and now the directive, along with line number information
         if (length $dir) {
             # the TAGS directive is a compile-time switch
-            if ($dir =~ /^TAGS\s+(.*)/i) {
+            if ($dir =~ /^$tags_dir\s+(.*)/) {
                 my @tags = split(/\s+/, $1);
                 if (scalar @tags > 1) {
                     ($start, $end) = map { quotemeta($_) } @tags;
@@ -9798,7 +9820,7 @@ sub split_text {
          : ( 'TEXT', $text) )
         if length $text;
         
-    return \@tokens;					    ## RETURN ##
+    return \@tokens;                                        ## RETURN ##
 }
     
 
@@ -9814,32 +9836,32 @@ sub interpolate_text {
            /
            ( (?: \\. | [^\$] ){1,3000} ) # escaped or non-'$' character [$1]
            |
-	   ( \$ (?:		    # embedded variable	           [$2]
-	     (?: \{ ([^\}]*) \} )   # ${ ... }                     [$3]
-	     |
-	     ([\w\.]+)		    # $word                        [$4]
-	     )
-	   )
-	/gx) {
-
-	($pre, $var, $dir) = ($1, $3 || $4, $2);
-
-	# preceding text
-	if (defined($pre) && length($pre)) {
-	    $line += $pre =~ tr/\n//;
-	    $pre =~ s/\\\$/\$/g;
-	    push(@tokens, 'TEXT', $pre);
-	}
-	# $variable reference
+           ( \$ (?:                 # embedded variable            [$2]
+             (?: \{ ([^\}]*) \} )   # ${ ... }                     [$3]
+             |
+             ([\w\.]+)              # $word                        [$4]
+             )
+           )
+        /gx) {
+
+        ($pre, $var, $dir) = ($1, $3 || $4, $2);
+
+        # preceding text
+        if (defined($pre) && length($pre)) {
+            $line += $pre =~ tr/\n//;
+            $pre =~ s/\\\$/\$/g;
+            push(@tokens, 'TEXT', $pre);
+        }
+        # $variable reference
         if ($var) {
-	    $line += $dir =~ tr/\n/ /;
-	    push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]);
-	}
-	# other '$' reference - treated as text
-	elsif ($dir) {
-	    $line += $dir =~ tr/\n//;
-	    push(@tokens, 'TEXT', $dir);
-	}
+            $line += $dir =~ tr/\n/ /;
+            push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]);
+        }
+        # other '$' reference - treated as text
+        elsif ($dir) {
+            $line += $dir =~ tr/\n//;
+            push(@tokens, 'TEXT', $dir);
+        }
     }
 
     return \@tokens;
@@ -9857,108 +9879,114 @@ sub tokenise_directive {
     my @tokens = ( );
 
     while ($text =~ 
-	    / 
-		# strip out any comments
-	        (\#[^\n]*)
-	   |
-		# a quoted phrase matches in $3
-		(["'])                   # $2 - opening quote, ' or "
-		(                        # $3 - quoted text buffer
-		    (?:                  # repeat group (no backreference)
-			\\\\             # an escaped backslash \\
-		    |                    # ...or...
-			\\\2             # an escaped quote \" or \' (match $1)
-		    |                    # ...or...
-			.                # any other character
-		    |	\n
-		    )*?                  # non-greedy repeat
-		)                        # end of $3
-		\2                       # match opening quote
-	    |
-		# an unquoted number matches in $4
-		(-?\d+(?:\.\d+)?)       # numbers
-	    |
-		# filename matches in $5
-	    	( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+)
-	    |
-		# an identifier matches in $6
-		(\w+)                    # variable identifier
-	    |   
-		# an unquoted word or symbol matches in $7
-		(   [(){}\[\]:;,\/\\]    # misc parenthesis and symbols
-		|   [+\-*]               # math operations
-		|   \$\{?                # dollar with option left brace
-		|   =>			 # like '='
-		|   [=!<>]?= | [!<>]     # eqality tests
-		|   &&? | \|\|?          # boolean ops
-		|   \.\.?                # n..n sequence
- 		|   \S+                  # something unquoted
-		)                        # end of $7
-	    /gmxo) {
-
-	# ignore comments to EOL
-	next if $1;
-
-	# quoted string
-	if (defined ($token = $3)) {
+            / 
+                # strip out any comments
+                (\#[^\n]*)
+           |
+                # a quoted phrase matches in $3
+                (["'])                   # $2 - opening quote, ' or "
+                (                        # $3 - quoted text buffer
+                    (?:                  # repeat group (no backreference)
+                        \\\\             # an escaped backslash \\
+                    |                    # ...or...
+                        \\\2             # an escaped quote \" or \' (match $1)
+                    |                    # ...or...
+                        .                # any other character
+                    |   \n
+                    )*?                  # non-greedy repeat
+                )                        # end of $3
+                \2                       # match opening quote
+            |
+                # an unquoted number matches in $4
+                (-?\d+(?:\.\d+)?)       # numbers
+            |
+                # filename matches in $5
+                ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+)
+            |
+                # an identifier matches in $6
+                (\w+)                    # variable identifier
+            |   
+                # an unquoted word or symbol matches in $7
+                (   [(){}\[\]:;,\/\\]    # misc parenthesis and symbols
+                |   [+\-*]               # math operations
+                |   \$\{?                # dollar with option left brace
+                |   =>                   # like '='
+                |   [=!<>]?= | [!<>]     # eqality tests
+                |   &&? | \|\|?          # boolean ops
+                |   \.\.?                # n..n sequence
+                |   \S+                  # something unquoted
+                )                        # end of $7
+            /gmxo) {
+
+        # ignore comments to EOL
+        next if $1;
+
+        # quoted string
+        if (defined ($token = $3)) {
             # double-quoted string may include $variable references
-	    if ($2 eq '"') {
-	        if ($token =~ /[\$\\]/) {
-		    $type = 'QUOTED';
-		    # unescape " and \ but leave \$ escaped so that 
-			# interpolate_text() doesn't incorrectly treat it
-		    # as a variable reference
-			for ($token) {
-				s/\\([^\$nrt])/$1/g;
-				s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge;
-			}
-		    push(@tokens, ('"') x 2,
-				  @{ $self->interpolate_text($token) },
-				  ('"') x 2);
-		    next;
-		}
+            if ($2 eq '"') {
+                if ($token =~ /[\$\\]/) {
+                    $type = 'QUOTED';
+                    # unescape " and \ but leave \$ escaped so that 
+                        # interpolate_text() doesn't incorrectly treat it
+                    # as a variable reference
+                        for ($token) {
+                                s/\\([^\$nrt])/$1/g;
+                                s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge;
+                        }
+                    push(@tokens, ('"') x 2,
+                                  @{ $self->interpolate_text($token) },
+                                  ('"') x 2);
+                    next;
+                }
                 else {
-	            $type = 'LITERAL';
-		    $token =~ s['][\\']g;
-		    $token = "'$token'";
-		}
-	    } 
-	    else {
-		$type = 'LITERAL';
-		$token = "'$token'";
-	    }
-	}
-	# number
-	elsif (defined ($token = $4)) {
-	    $type = 'NUMBER';
-	}
-	elsif (defined($token = $5)) {
-	    $type = 'FILENAME';
-	}
-	elsif (defined($token = $6)) {
-	    # reserved words may be in lower case unless case sensitive
-	    $uctoken = $anycase ? uc $token : $token;
-	    if (defined ($type = $lextable->{ $uctoken })) {
-		$token = $uctoken;
-	    }
-	    else {
-		$type = 'IDENT';
-	    }
-	}
-	elsif (defined ($token = $7)) {
-	    # reserved words may be in lower case unless case sensitive
-	    $uctoken = $anycase ? uc $token : $token;
-	    unless (defined ($type = $lextable->{ $uctoken })) {
-		$type = 'UNQUOTED';
-	    }
-	}
+                    $type = 'LITERAL';
+                    $token =~ s['][\\']g;
+                    $token = "'$token'";
+                }
+            } 
+            else {
+                $type = 'LITERAL';
+                $token = "'$token'";
+            }
+        }
+        # number
+        elsif (defined ($token = $4)) {
+            $type = 'NUMBER';
+        }
+        elsif (defined($token = $5)) {
+            $type = 'FILENAME';
+        }
+        elsif (defined($token = $6)) {
+            # Fold potential keywords to UPPER CASE if the ANYCASE option is
+            # set, unless (we've got some preceeding tokens and) the previous
+            # token is a DOT op.  This prevents the 'last' in 'data.last'
+            # from being interpreted as the LAST keyword.
+            $uctoken = 
+                ($anycase && (! @tokens || $tokens[-2] ne 'DOT'))
+                    ? uc $token
+                    :    $token;
+            if (defined ($type = $lextable->{ $uctoken })) {
+                $token = $uctoken;
+            }
+            else {
+                $type = 'IDENT';
+            }
+        }
+        elsif (defined ($token = $7)) {
+            # reserved words may be in lower case unless case sensitive
+            $uctoken = $anycase ? uc $token : $token;
+            unless (defined ($type = $lextable->{ $uctoken })) {
+                $type = 'UNQUOTED';
+            }
+        }
 
-	push(@tokens, $type, $token);
+        push(@tokens, $type, $token);
 
     }
 
 
-    return \@tokens;					    ## RETURN ##
+    return \@tokens;                                        ## RETURN ##
 }
 
 
@@ -9969,7 +9997,7 @@ sub define_block {
         || return undef;
 
     $self->debug("compiled block '$name':\n$block")
-	if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
+        if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
 
     $defblock->{ $name } = $block;
     
@@ -10014,6 +10042,7 @@ sub location {
     my $file = $info->{ path } || $info->{ name } 
         || '(unknown template)';
     $line =~ s/\-.*$//; # might be 'n-n'
+    $line ||= 1;
     return "#line $line \"$file\"\n";
 }
 
@@ -10024,7 +10053,7 @@ sub _parse {
     my ($self, $tokens, $info) = @_;
     my ($token, $value, $text, $line, $inperl);
     my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars);
-    my ($lhs, $len, $code);	    # rule contents
+    my ($lhs, $len, $code);         # rule contents
     my $stack = [ [ 0, undef ] ];   # DFA stack
 
 
@@ -10043,152 +10072,152 @@ sub _parse {
     my $in_string = 0;
 
     while(1) {
-	# get state number and state
-	$stateno =  $stack->[-1]->[0];
-	$state   = $states->[$stateno];
-
-	# see if any lookaheads exist for the current state
-	if (exists $state->{'ACTIONS'}) {
-
-	    # get next token and expand any directives (i.e. token is an 
-	    # array ref) onto the front of the token list
-	    while (! defined $token && @$tokens) {
-		$token = shift(@$tokens);
-		if (ref $token) {
-		    ($text, $line, $token) = @$token;
-		    if (ref $token) {
-			if ($info->{ DEBUG } && ! $in_string) {
+        # get state number and state
+        $stateno =  $stack->[-1]->[0];
+        $state   = $states->[$stateno];
+
+        # see if any lookaheads exist for the current state
+        if (exists $state->{'ACTIONS'}) {
+
+            # get next token and expand any directives (i.e. token is an 
+            # array ref) onto the front of the token list
+            while (! defined $token && @$tokens) {
+                $token = shift(@$tokens);
+                if (ref $token) {
+                    ($text, $line, $token) = @$token;
+                    if (ref $token) {
+                        if ($info->{ DEBUG } && ! $in_string) {
                             # - - - - - - - - - - - - - - - - - - - - - - - - -
-			    # This is gnarly.  Look away now if you're easily
+                            # This is gnarly.  Look away now if you're easily
                             # frightened.  We're pushing parse tokens onto the
                             # pending list to simulate a DEBUG directive like so:
-			    # [% DEBUG msg line='20' text='INCLUDE foo' %]
+                            # [% DEBUG msg line='20' text='INCLUDE foo' %]
                             # - - - - - - - - - - - - - - - - - - - - - - - - -
-			    my $dtext = $text;
-			    $dtext =~ s[(['\\])][\\$1]g;
-			    unshift(@$tokens, 
-				    DEBUG   => 'DEBUG',
-				    IDENT   => 'msg',
-				    IDENT   => 'line',
-				    ASSIGN  => '=',
-				    LITERAL => "'$line'",
-				    IDENT   => 'text',
-				    ASSIGN  => '=',
-				    LITERAL => "'$dtext'",
-				    IDENT   => 'file',
-				    ASSIGN  => '=',
-				    LITERAL => "'$info->{ name }'",
-				    (';') x 2,
-				    @$token, 
-				    (';') x 2);
-			}
-			else {
-			    unshift(@$tokens, @$token, (';') x 2);
-			}
-			$token = undef;  # force redo
-		    }
-		    elsif ($token eq 'ITEXT') {
-			if ($inperl) {
-			    # don't perform interpolation in PERL blocks
-			    $token = 'TEXT';
-			    $value = $text;
-			}
-			else {
-			    unshift(@$tokens, 
-				    @{ $self->interpolate_text($text, $line) });
-			    $token = undef; # force redo
-			}
-		    }
-		}
-		else {
-		    # toggle string flag to indicate if we're crossing
-		    # a string boundary
-		    $in_string = ! $in_string if $token eq '"';
-		    $value = shift(@$tokens);
-		}
-	    };
-	    # clear undefined token to avoid 'undefined variable blah blah'
-	    # warnings and let the parser logic pick it up in a minute
-	    $token = '' unless defined $token;
-
-	    # get the next state for the current lookahead token
-	    $action = defined ($lookup = $state->{'ACTIONS'}->{ $token })
-	              ? $lookup
-		      : defined ($lookup = $state->{'DEFAULT'})
-		        ? $lookup
-		        : undef;
-	}
-	else {
-	    # no lookahead actions
-	    $action = $state->{'DEFAULT'};
-	}
+                            my $dtext = $text;
+                            $dtext =~ s[(['\\])][\\$1]g;
+                            unshift(@$tokens, 
+                                    DEBUG   => 'DEBUG',
+                                    IDENT   => 'msg',
+                                    IDENT   => 'line',
+                                    ASSIGN  => '=',
+                                    LITERAL => "'$line'",
+                                    IDENT   => 'text',
+                                    ASSIGN  => '=',
+                                    LITERAL => "'$dtext'",
+                                    IDENT   => 'file',
+                                    ASSIGN  => '=',
+                                    LITERAL => "'$info->{ name }'",
+                                    (';') x 2,
+                                    @$token, 
+                                    (';') x 2);
+                        }
+                        else {
+                            unshift(@$tokens, @$token, (';') x 2);
+                        }
+                        $token = undef;  # force redo
+                    }
+                    elsif ($token eq 'ITEXT') {
+                        if ($inperl) {
+                            # don't perform interpolation in PERL blocks
+                            $token = 'TEXT';
+                            $value = $text;
+                        }
+                        else {
+                            unshift(@$tokens, 
+                                    @{ $self->interpolate_text($text, $line) });
+                            $token = undef; # force redo
+                        }
+                    }
+                }
+                else {
+                    # toggle string flag to indicate if we're crossing
+                    # a string boundary
+                    $in_string = ! $in_string if $token eq '"';
+                    $value = shift(@$tokens);
+                }
+            };
+            # clear undefined token to avoid 'undefined variable blah blah'
+            # warnings and let the parser logic pick it up in a minute
+            $token = '' unless defined $token;
+
+            # get the next state for the current lookahead token
+            $action = defined ($lookup = $state->{'ACTIONS'}->{ $token })
+                      ? $lookup
+                      : defined ($lookup = $state->{'DEFAULT'})
+                        ? $lookup
+                        : undef;
+        }
+        else {
+            # no lookahead actions
+            $action = $state->{'DEFAULT'};
+        }
 
-	# ERROR: no ACTION
-	last unless defined $action;
+        # ERROR: no ACTION
+        last unless defined $action;
 
-	# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-	# shift (+ive ACTION)
-	# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-	if ($action > 0) {
-	    push(@$stack, [ $action, $value ]);
-	    $token = $value = undef;
-	    redo;
-	};
+        # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        # shift (+ive ACTION)
+        # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        if ($action > 0) {
+            push(@$stack, [ $action, $value ]);
+            $token = $value = undef;
+            redo;
+        };
 
-	# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-	# reduce (-ive ACTION)
-	# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-	($lhs, $len, $code) = @{ $rules->[ -$action ] };
+        # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        # reduce (-ive ACTION)
+        # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        ($lhs, $len, $code) = @{ $rules->[ -$action ] };
 
-	# no action imples ACCEPTance
-	$action
-	    or $status = ACCEPT;
+        # no action imples ACCEPTance
+        $action
+            or $status = ACCEPT;
 
-	# use dummy sub if code ref doesn't exist
-	$code = sub { $_[1] }
-	    unless $code;
+        # use dummy sub if code ref doesn't exist
+        $code = sub { $_[1] }
+            unless $code;
 
-	@codevars = $len
-		?   map { $_->[1] } @$stack[ -$len .. -1 ]
-		:   ();
+        @codevars = $len
+                ?   map { $_->[1] } @$stack[ -$len .. -1 ]
+                :   ();
 
-	eval {
-	    $coderet = &$code( $self, @codevars );
-	};
-	if ($@) {
-	    my $err = $@;
-	    chomp $err;
-	    return $self->_parse_error($err);
-	}
+        eval {
+            $coderet = &$code( $self, @codevars );
+        };
+        if ($@) {
+            my $err = $@;
+            chomp $err;
+            return $self->_parse_error($err);
+        }
 
-	# reduce stack by $len
-	splice(@$stack, -$len, $len);
+        # reduce stack by $len
+        splice(@$stack, -$len, $len);
 
-	# ACCEPT
-	return $coderet					    ## RETURN ##
-	    if $status == ACCEPT;
+        # ACCEPT
+        return $coderet                                     ## RETURN ##
+            if $status == ACCEPT;
 
-	# ABORT
-	return undef					    ## RETURN ##
-	    if $status == ABORT;
+        # ABORT
+        return undef                                        ## RETURN ##
+            if $status == ABORT;
 
-	# ERROR
-	last 
-	    if $status == ERROR;
+        # ERROR
+        last 
+            if $status == ERROR;
     }
     continue {
-	push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, 
-	      $coderet ]), 
+        push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, 
+              $coderet ]), 
     }
 
-    # ERROR						    ## RETURN ##
+    # ERROR                                                 ## RETURN ##
     return $self->_parse_error('unexpected end of input')
-	unless defined $value;
+        unless defined $value;
 
     # munge text of last directive to make it readable
 
     return $self->_parse_error("unexpected end of directive", $text)
-	if $value eq ';';   # end of directive SEPARATOR
+        if $value eq ';';   # end of directive SEPARATOR
 
     return $self->_parse_error("unexpected token ($value)", $text);
 }
@@ -10203,7 +10232,7 @@ sub _parse_error {
     $line = 'unknown' unless $line;
 
     $msg .= "\n  [% $text %]"
-	if defined $text;
+        if defined $text;
 
     return $self->error("line $line: $msg");
 }
@@ -10217,10 +10246,10 @@ sub _dump {
     my $key;
 
     foreach $key (qw( START_TAG END_TAG TAG_STYLE ANYCASE INTERPOLATE 
-		      PRE_CHOMP POST_CHOMP V1DOLLAR )) {
-	my $val = $self->{ $key };
-	$val = '<undef>' unless defined $val;
-	$output .= sprintf($format, $key, $val);
+                      PRE_CHOMP POST_CHOMP V1DOLLAR )) {
+        my $val = $self->{ $key };
+        $val = '<undef>' unless defined $val;
+        $output .= sprintf($format, $key, $val);
     }
 
     $output .= '}';
@@ -10250,7 +10279,7 @@ our $INJAVASCRIPT = 0;
 sub template {
     my ($class, $block) = @_;
 
-    return "sub { return '' }" unless $block =~ /\S/;
+    return "function() { return ''; }" unless $block =~ /\S/;
 
     return <<"...";
 function(context) {
@@ -10271,6 +10300,16 @@ $block
 ...
 }
  
+sub _attempt_range_expand_val ($) {
+    my $val = shift;
+    return $val unless
+        my ( $from, $to ) = $val =~ m/\s*\[\s*(\S+)\s*\.\.\s*(\S+)\s*\]/;
+
+    die "Range expansion is current supported for positive/negative integer values only (e.g. [ 1 .. 10 ])\nCannot expand: $val" unless $from =~ m/^-?\d+$/ && $to =~ m/^-?\d+$/;
+
+    return join '', '[', join( ',', $from .. $to ), ']';
+}
+
 
 sub textblock {
     my ($class, $text) = @_;
@@ -10327,6 +10366,7 @@ sub assign {
             $var = '[' . join(', ', @$var) . ']';
         }
     }
+    $val =  _attempt_range_expand_val $val;
     $val .= ', 1' if $default;
     return "stash.set($var, $val)";
 }
@@ -10465,6 +10505,8 @@ sub foreach {
         $loop_restore = 'stash = context.delocalise()';
     }
 
+    $list = _attempt_range_expand_val $list;
+
     return <<EOF;
 
 // FOREACH 
@@ -10665,6 +10707,19 @@ sub use {
 
 
 
+sub raw {
+    my ($class, $lnameargs) = @_;
+    my ($file, $args, $alias) = @$lnameargs;
+    $file = shift @$file;       # same production rule as INCLUDE
+    $alias ||= $file;
+    $args = &args($class, $args);
+    $file =~ s/'|"//g;
+    return "// RAW\n"
+         . "stash.set($alias, $file);";
+}
+
+
+
 sub filter {
     my ($class, $lnameargs, $block) = @_;
     my ($name, $args, $alias) = @$lnameargs;
@@ -10694,12 +10749,97 @@ sub quoted {
     return "throw('QUOTED called with unknown arguments in Jemplate');";
 }   
 
+
 sub macro {
-    return "throw('MACRO not yet supported in Jemplate');";
-}   
+    my ($class, $ident, $block, $args) = @_;
+
+    if ($args) {
+        $args = join(';', map { "args['$_'] = fargs.shift()" } @$args);
+
+        return <<EOF;
+
+//MACRO
+stash.set('$ident', function () {
+    var output = '';
+    var args = {};
+    var fargs = Array.prototype.slice.call(arguments);
+    $args;
+    args.arguments = Array.prototype.slice.call(arguments);
+
+    var params = fargs.shift() || {};
+
+    for (var key in params) {
+        args[key] = params[key];
+    }
+
+    context.stash.clone(args);
+    try {
+$block
+    }
+    catch(e) {
+        var error = context.set_error(e, output);
+        throw(error);
+    }
+
+    context.stash.declone();
+    return output;
+});
+
+EOF
+
+    }
+    else {
+        return <<EOF;
+
+//MACRO
+
+stash.set('$ident', function () {
+    var output = '';
+    var args = {};
+    
+    var fargs = Array.prototype.slice.call(arguments);
+    args.arguments = Array.prototype.slice.call(arguments);   
+    
+    if (typeof arguments[0] == 'object') args = arguments[0];
+    
+    context.stash.clone(args);
+    try {
+$block
+    }
+    catch(e) {
+        var error = context.set_error(e, output);
+        throw(error);
+    }
+
+    context.stash.declone(); 
+    return output;});
+
+EOF
+    }
+}
 
 sub capture {
-    return "throw('CAPTURE not yet supported in Jemplate');";
+    my ($class, $name, $block) = @_;
+
+    if (ref $name) {
+        if (scalar @$name == 2 && ! $name->[1]) {
+            $name = $name->[0];
+        }
+        else {
+            $name = '[' . join(', ', @$name) . ']';
+        }
+    }
+
+    return <<EOF;
+
+// CAPTURE
+(function() {
+	var output = '';
+	$block
+	stash.set($name, output);
+})();
+EOF
+
 }   
 
 BEGIN {
@@ -10749,7 +10889,7 @@ my ($factory, $rawstart);
 
 @RESERVED = qw( 
 	GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER BLOCK END
-	USE PLUGIN FILTER MACRO JAVASCRIPT TO STEP AND OR NOT DIV MOD
+	USE RAW PLUGIN FILTER MACRO JAVASCRIPT TO STEP AND OR NOT DIV MOD
 	IF UNLESS ELSE ELSIF FOR NEXT WHILE SWITCH CASE META IN
 	TRY THROW CATCH FINAL LAST RETURN STOP CLEAR VIEW DEBUG
     );
@@ -10819,187 +10959,189 @@ $STATES = [
 	{#State 0
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
-			'node' => 23,
-			'term' => 58,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'template' => 51,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'template' => 53,
 			'defblockname' => 14,
-			'filter' => 29,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'switch' => 34,
-			'try' => 35,
 			'assign' => 19,
-			'block' => 72,
-			'directive' => 71,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'lterm' => 58,
+			'node' => 23,
+			'term' => 60,
+			'expr' => 64,
+			'use' => 65,
+			'defblock' => 68,
+			'filter' => 30,
+			'sterm' => 70,
+			'chunks' => 34,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
+			'directive' => 73,
+			'block' => 74,
+			'raw' => 39,
+			'condition' => 75
 		}
 	},
 	{#State 1
 		ACTIONS => {
-			"\$" => 42,
-			'LITERAL' => 75,
+			"\$" => 44,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'setlist' => 76,
-			'item' => 39,
+			'setlist' => 78,
+			'item' => 41,
 			'assign' => 19,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
 	{#State 2
-		DEFAULT => -129
+		DEFAULT => -131
 	},
 	{#State 3
-		DEFAULT => -25
+		DEFAULT => -26
 	},
 	{#State 4
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 79,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 81,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
 	{#State 5
-		DEFAULT => -23
+		DEFAULT => -24
 	},
 	{#State 6
 		ACTIONS => {
-			";" => 80
+			";" => 82
 		}
 	},
 	{#State 7
-		DEFAULT => -38
+		DEFAULT => -39
 	},
 	{#State 8
-		DEFAULT => -14
+		DEFAULT => -15
 	},
 	{#State 9
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 90,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 92,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
 	{#State 10
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"]" => 94,
-			"\${" => 37
+			"]" => 96,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 96,
-			'item' => 39,
-			'range' => 93,
+			'sterm' => 98,
+			'item' => 41,
+			'range' => 95,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 95,
-			'lterm' => 56,
-			'list' => 92
+			'ident' => 79,
+			'term' => 97,
+			'lterm' => 58,
+			'list' => 94
 		}
 	},
 	{#State 11
 		ACTIONS => {
-			";" => 97
+			";" => 99
 		}
 	},
 	{#State 12
@@ -11007,1200 +11149,1209 @@ $STATES = [
 	},
 	{#State 13
 		ACTIONS => {
-			";" => -20
+			";" => -21
 		},
-		DEFAULT => -28
+		DEFAULT => -29
 	},
 	{#State 14
 		ACTIONS => {
-			'IDENT' => 98
+			'IDENT' => 100
 		},
-		DEFAULT => -86,
+		DEFAULT => -88,
 		GOTOS => {
-			'blockargs' => 101,
-			'metadata' => 100,
-			'meta' => 99
+			'blockargs' => 103,
+			'metadata' => 102,
+			'meta' => 101
 		}
 	},
 	{#State 15
 		ACTIONS => {
-			'IDENT' => 98
+			'IDENT' => 100
 		},
 		GOTOS => {
-			'metadata' => 102,
-			'meta' => 99
+			'metadata' => 104,
+			'meta' => 101
 		}
 	},
 	{#State 16
 		ACTIONS => {
-			'DOT' => 103,
-			'ASSIGN' => 104
+			'DOT' => 105,
+			'ASSIGN' => 106
 		},
-		DEFAULT => -108
+		DEFAULT => -110
 	},
 	{#State 17
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 105,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 107,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
 	{#State 18
 		ACTIONS => {
-			'IDENT' => 106
+			'IDENT' => 108
 		}
 	},
 	{#State 19
-		DEFAULT => -148
+		DEFAULT => -150
 	},
 	{#State 20
 		DEFAULT => -12
 	},
 	{#State 21
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
-			'IDENT' => 107,
-			"\"" => 60,
-			"\$" => 42,
+			"{" => 31,
+			'LITERAL' => 80,
+			'IDENT' => 109,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
-			'loopvar' => 109,
+			'sterm' => 70,
+			'item' => 41,
+			'loopvar' => 111,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 108,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 110,
+			'lterm' => 58
 		}
 	},
 	{#State 22
-		DEFAULT => -41
+		DEFAULT => -42
 	},
 	{#State 23
-		DEFAULT => -126
+		DEFAULT => -128
 	},
 	{#State 24
 		DEFAULT => -6
 	},
 	{#State 25
 		ACTIONS => {
-			"\"" => 116,
-			"\$" => 113,
-			'LITERAL' => 115,
-			'FILENAME' => 83,
-			'IDENT' => 110,
-			'NUMBER' => 84,
-			"\${" => 37
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
 		GOTOS => {
-			'names' => 91,
-			'lvalue' => 111,
-			'item' => 112,
-			'name' => 82,
-			'filepart' => 87,
-			'filename' => 85,
-			'nameargs' => 117,
-			'lnameargs' => 114
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 116
 		}
 	},
 	{#State 26
-		DEFAULT => -112
+		DEFAULT => -114
 	},
 	{#State 27
 		ACTIONS => {
-			"\$" => 42,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 118
+			'ident' => 120
 		}
 	},
 	{#State 28
 		ACTIONS => {
-			'LITERAL' => 123,
-			'FILENAME' => 83,
-			'IDENT' => 119,
-			'NUMBER' => 84
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
-		DEFAULT => -86,
 		GOTOS => {
-			'blockargs' => 122,
-			'filepart' => 87,
-			'filename' => 121,
-			'blockname' => 120,
-			'metadata' => 100,
-			'meta' => 99
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 121
 		}
 	},
 	{#State 29
-		DEFAULT => -44
+		ACTIONS => {
+			'LITERAL' => 126,
+			'FILENAME' => 85,
+			'IDENT' => 122,
+			'NUMBER' => 86
+		},
+		DEFAULT => -88,
+		GOTOS => {
+			'blockargs' => 125,
+			'filepart' => 89,
+			'filename' => 124,
+			'blockname' => 123,
+			'metadata' => 102,
+			'meta' => 101
+		}
 	},
 	{#State 30
+		DEFAULT => -45
+	},
+	{#State 31
 		ACTIONS => {
-			"\$" => 42,
-			'LITERAL' => 128,
+			"\$" => 44,
+			'LITERAL' => 131,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -118,
+		DEFAULT => -120,
 		GOTOS => {
-			'params' => 127,
-			'hash' => 124,
-			'item' => 125,
-			'param' => 126
+			'params' => 130,
+			'hash' => 127,
+			'item' => 128,
+			'param' => 129
 		}
 	},
-	{#State 31
-		DEFAULT => -26
-	},
 	{#State 32
+		DEFAULT => -27
+	},
+	{#State 33
 		ACTIONS => {
-			"\"" => 116,
-			"\$" => 113,
-			'LITERAL' => 115,
-			'FILENAME' => 83,
-			'IDENT' => 110,
-			'NUMBER' => 84,
-			"\${" => 37
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
 		GOTOS => {
-			'names' => 91,
-			'lvalue' => 111,
-			'item' => 112,
-			'name' => 82,
-			'filepart' => 87,
-			'filename' => 85,
-			'nameargs' => 117,
-			'lnameargs' => 129
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 132
 		}
 	},
-	{#State 33
+	{#State 34
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -2,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
-			'chunk' => 130,
-			'defblock' => 66,
+			'wrapper' => 47,
+			'atomexpr' => 49,
+			'chunk' => 133,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 71,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 34
-		DEFAULT => -22
-	},
 	{#State 35
-		DEFAULT => -24
+		DEFAULT => -23
 	},
 	{#State 36
+		DEFAULT => -25
+	},
+	{#State 37
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 131,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 134,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 37
+	{#State 38
 		ACTIONS => {
-			"\"" => 60,
-			"\$" => 42,
-			'LITERAL' => 78,
+			"\"" => 62,
+			"\$" => 44,
+			'LITERAL' => 80,
 			'IDENT' => 2,
 			'REF' => 27,
 			'NUMBER' => 26,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 132,
-			'item' => 39,
+			'sterm' => 135,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77
+			'ident' => 79
 		}
 	},
-	{#State 38
+	{#State 39
+		DEFAULT => -14
+	},
+	{#State 40
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 133,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 136,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 39
+	{#State 41
 		ACTIONS => {
-			"(" => 134
+			"(" => 137
 		},
-		DEFAULT => -127
+		DEFAULT => -129
 	},
-	{#State 40
-		DEFAULT => -39
+	{#State 42
+		DEFAULT => -40
 	},
-	{#State 41
+	{#State 43
 		DEFAULT => -11
 	},
-	{#State 42
+	{#State 44
 		ACTIONS => {
-			'IDENT' => 135
+			'IDENT' => 138
 		}
 	},
-	{#State 43
+	{#State 45
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 136,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 139,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 44
+	{#State 46
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 137,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 140,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 45
-		DEFAULT => -43
+	{#State 47
+		DEFAULT => -44
 	},
-	{#State 46
+	{#State 48
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 138,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 141,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 47
+	{#State 49
 		ACTIONS => {
-			'IF' => 142,
-			'FILTER' => 141,
-			'FOR' => 140,
-			'WHILE' => 144,
-			'WRAPPER' => 143,
-			'UNLESS' => 139
+			'IF' => 145,
+			'FILTER' => 144,
+			'FOR' => 143,
+			'WHILE' => 147,
+			'WRAPPER' => 146,
+			'UNLESS' => 142
 		}
 	},
-	{#State 48
-		DEFAULT => -40
+	{#State 50
+		DEFAULT => -41
 	},
-	{#State 49
+	{#State 51
 		DEFAULT => -10
 	},
-	{#State 50
+	{#State 52
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 145,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 148,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 51
+	{#State 53
 		ACTIONS => {
-			'' => 146
+			'' => 149
 		}
 	},
-	{#State 52
+	{#State 54
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 57,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 59,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 149,
-			'sterm' => 68,
-			'item' => 39,
-			'assign' => 148,
+			'expr' => 152,
+			'sterm' => 70,
+			'item' => 41,
+			'assign' => 151,
 			'node' => 23,
-			'ident' => 147,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 150,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 53
+	{#State 55
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 150,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 153,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 54
+	{#State 56
 		ACTIONS => {
-			";" => 151
+			";" => 154
 		}
 	},
-	{#State 55
+	{#State 57
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 152,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 155,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 56
-		DEFAULT => -102
+	{#State 58
+		DEFAULT => -104
 	},
-	{#State 57
+	{#State 59
 		ACTIONS => {
-			'ASSIGN' => 153
+			'ASSIGN' => 156
 		},
-		DEFAULT => -111
+		DEFAULT => -113
 	},
-	{#State 58
-		DEFAULT => -145
+	{#State 60
+		DEFAULT => -147
 	},
-	{#State 59
-		DEFAULT => -15
+	{#State 61
+		DEFAULT => -16
 	},
-	{#State 60
-		DEFAULT => -175,
+	{#State 62
+		DEFAULT => -177,
 		GOTOS => {
-			'quoted' => 154
+			'quoted' => 157
 		}
 	},
-	{#State 61
+	{#State 63
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 155,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 158,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 62
+	{#State 64
 		ACTIONS => {
-			";" => -16,
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			";" => -17,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -27
+		DEFAULT => -28
 	},
-	{#State 63
+	{#State 65
 		DEFAULT => -13
 	},
-	{#State 64
-		DEFAULT => -37
+	{#State 66
+		DEFAULT => -38
 	},
-	{#State 65
+	{#State 67
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 166,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 169,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 66
+	{#State 68
 		DEFAULT => -9
 	},
-	{#State 67
+	{#State 69
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 167,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 170,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 68
-		DEFAULT => -103
+	{#State 70
+		DEFAULT => -105
 	},
-	{#State 69
+	{#State 71
 		ACTIONS => {
-			"\$" => 42,
-			'LITERAL' => 75,
+			"\$" => 44,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'setlist' => 168,
-			'item' => 39,
+			'setlist' => 171,
+			'item' => 41,
 			'assign' => 19,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 70
+	{#State 72
 		ACTIONS => {
-			"\$" => 42,
-			'COMMA' => 170,
-			'LITERAL' => 75,
+			"\$" => 44,
+			'COMMA' => 173,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -19,
+		DEFAULT => -20,
 		GOTOS => {
-			'item' => 39,
-			'assign' => 169,
+			'item' => 41,
+			'assign' => 172,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 71
+	{#State 73
 		DEFAULT => -8
 	},
-	{#State 72
+	{#State 74
 		DEFAULT => -1
 	},
-	{#State 73
-		DEFAULT => -21
+	{#State 75
+		DEFAULT => -22
 	},
-	{#State 74
+	{#State 76
 		ACTIONS => {
-			'ASSIGN' => 171,
-			'DOT' => 103
+			'ASSIGN' => 174,
+			'DOT' => 105
 		}
 	},
-	{#State 75
+	{#State 77
 		ACTIONS => {
-			'ASSIGN' => 153
+			'ASSIGN' => 156
 		}
 	},
-	{#State 76
+	{#State 78
 		ACTIONS => {
-			'COMMA' => 170,
-			'LITERAL' => 75,
+			'COMMA' => 173,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\$" => 42,
-			"\${" => 37
+			"\$" => 44,
+			"\${" => 38
 		},
-		DEFAULT => -31,
+		DEFAULT => -32,
 		GOTOS => {
-			'item' => 39,
-			'assign' => 169,
+			'item' => 41,
+			'assign' => 172,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 77
-		ACTIONS => {
-			'DOT' => 103
-		},
-		DEFAULT => -108
-	},
-	{#State 78
-		DEFAULT => -111
-	},
 	{#State 79
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 172,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
-		}
+			'DOT' => 105
+		},
+		DEFAULT => -110
 	},
 	{#State 80
-		DEFAULT => -7
+		DEFAULT => -113
 	},
 	{#State 81
-		DEFAULT => -172
+		ACTIONS => {
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 175,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
+		}
 	},
 	{#State 82
-		DEFAULT => -165
+		DEFAULT => -7
 	},
 	{#State 83
-		DEFAULT => -171
+		DEFAULT => -174
 	},
 	{#State 84
-		DEFAULT => -173
+		DEFAULT => -167
 	},
 	{#State 85
+		DEFAULT => -173
+	},
+	{#State 86
+		DEFAULT => -175
+	},
+	{#State 87
 		ACTIONS => {
-			'DOT' => 173
+			'DOT' => 176
 		},
-		DEFAULT => -167
+		DEFAULT => -169
 	},
-	{#State 86
+	{#State 88
 		ACTIONS => {
-			"\$" => 42,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 174
+			'ident' => 177
 		}
 	},
-	{#State 87
-		DEFAULT => -170
+	{#State 89
+		DEFAULT => -172
 	},
-	{#State 88
-		DEFAULT => -168
+	{#State 90
+		DEFAULT => -170
 	},
-	{#State 89
-		DEFAULT => -175,
+	{#State 91
+		DEFAULT => -177,
 		GOTOS => {
-			'quoted' => 175
+			'quoted' => 178
 		}
 	},
-	{#State 90
-		DEFAULT => -36
+	{#State 92
+		DEFAULT => -37
 	},
-	{#State 91
+	{#State 93
 		ACTIONS => {
-			"+" => 176,
-			"(" => 177
+			"+" => 179,
+			"(" => 180
 		},
-		DEFAULT => -155,
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 178
+			'args' => 181
 		}
 	},
-	{#State 92
+	{#State 94
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 181,
-			'LITERAL' => 78,
+			"{" => 31,
+			'COMMA' => 184,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"]" => 179,
-			"\${" => 37
+			"]" => 182,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 180,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 183,
+			'lterm' => 58
 		}
 	},
-	{#State 93
+	{#State 95
 		ACTIONS => {
-			"]" => 182
+			"]" => 185
 		}
 	},
-	{#State 94
-		DEFAULT => -106
+	{#State 96
+		DEFAULT => -108
 	},
-	{#State 95
-		DEFAULT => -115
+	{#State 97
+		DEFAULT => -117
 	},
-	{#State 96
+	{#State 98
 		ACTIONS => {
-			'TO' => 183
+			'TO' => 186
 		},
-		DEFAULT => -103
+		DEFAULT => -105
 	},
-	{#State 97
+	{#State 99
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 184,
-			'directive' => 71,
+			'block' => 187,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 98
+	{#State 100
 		ACTIONS => {
-			'ASSIGN' => 185
+			'ASSIGN' => 188
 		}
 	},
-	{#State 99
-		DEFAULT => -98
-	},
-	{#State 100
+	{#State 101
+		DEFAULT => -100
+	},
+	{#State 102
 		ACTIONS => {
-			'COMMA' => 187,
-			'IDENT' => 98
+			'COMMA' => 190,
+			'IDENT' => 100
 		},
-		DEFAULT => -85,
+		DEFAULT => -87,
 		GOTOS => {
-			'meta' => 186
+			'meta' => 189
 		}
 	},
-	{#State 101
+	{#State 103
 		ACTIONS => {
-			";" => 188
+			";" => 191
 		}
 	},
-	{#State 102
+	{#State 104
 		ACTIONS => {
-			'COMMA' => 187,
-			'IDENT' => 98
+			'COMMA' => 190,
+			'IDENT' => 100
 		},
-		DEFAULT => -17,
+		DEFAULT => -18,
 		GOTOS => {
-			'meta' => 186
+			'meta' => 189
 		}
 	},
-	{#State 103
+	{#State 105
 		ACTIONS => {
-			"\$" => 42,
+			"\$" => 44,
 			'IDENT' => 2,
-			'NUMBER' => 190,
-			"\${" => 37
+			'NUMBER' => 193,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
-			'node' => 189
+			'item' => 41,
+			'node' => 192
 		}
 	},
-	{#State 104
+	{#State 106
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
+			"(" => 54,
+			'SWITCH' => 55,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
 			'FOR' => 21,
 			'NEXT' => 22,
-			'LITERAL' => 57,
-			"\"" => 60,
-			'PROCESS' => 61,
+			'LITERAL' => 59,
+			"\"" => 62,
+			'PROCESS' => 63,
 			'FILTER' => 25,
-			'RETURN' => 64,
-			'INSERT' => 65,
+			'RETURN' => 66,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 191,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			"\${" => 37
+			'WHILE' => 69,
+			'BLOCK' => 194,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'expr' => 193,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'expr' => 196,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'atomdir' => 13,
-			'mdir' => 192,
-			'filter' => 29,
-			'sterm' => 68,
-			'ident' => 147,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'mdir' => 195,
+			'filter' => 30,
+			'sterm' => 70,
+			'ident' => 150,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 194,
-			'condition' => 73,
-			'lterm' => 56
+			'directive' => 197,
+			'condition' => 75,
+			'lterm' => 58
 		}
 	},
-	{#State 105
-		DEFAULT => -34
+	{#State 107
+		DEFAULT => -35
 	},
-	{#State 106
+	{#State 108
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'INCLUDE' => 17,
-			"(" => 196,
-			'SWITCH' => 53,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
+			"(" => 199,
+			'SWITCH' => 55,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
 			'FOR' => 21,
 			'NEXT' => 22,
-			'LITERAL' => 57,
-			"\"" => 60,
-			'PROCESS' => 61,
+			'LITERAL' => 59,
+			"\"" => 62,
+			'PROCESS' => 63,
 			'FILTER' => 25,
-			'RETURN' => 64,
-			'INSERT' => 65,
+			'RETURN' => 66,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 191,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			"\${" => 37
+			'WHILE' => 69,
+			'BLOCK' => 194,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'expr' => 197,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'expr' => 200,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'atomdir' => 13,
-			'mdir' => 195,
-			'filter' => 29,
-			'sterm' => 68,
-			'ident' => 147,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'mdir' => 198,
+			'filter' => 30,
+			'sterm' => 70,
+			'ident' => 150,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 194,
-			'condition' => 73,
-			'lterm' => 56
+			'directive' => 197,
+			'condition' => 75,
+			'lterm' => 58
 		}
 	},
-	{#State 107
+	{#State 109
 		ACTIONS => {
-			'IN' => 199,
-			'ASSIGN' => 198
+			'IN' => 202,
+			'ASSIGN' => 201
 		},
-		DEFAULT => -129
+		DEFAULT => -131
 	},
-	{#State 108
-		DEFAULT => -155,
+	{#State 110
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 200
+			'args' => 203
 		}
 	},
-	{#State 109
+	{#State 111
 		ACTIONS => {
-			";" => 201
+			";" => 204
 		}
 	},
-	{#State 110
+	{#State 112
 		ACTIONS => {
-			'ASSIGN' => -129
+			'ASSIGN' => -131
 		},
-		DEFAULT => -172
+		DEFAULT => -174
 	},
-	{#State 111
+	{#State 113
 		ACTIONS => {
-			'ASSIGN' => 202
+			'ASSIGN' => 205
 		}
 	},
-	{#State 112
-		DEFAULT => -158
+	{#State 114
+		DEFAULT => -160
 	},
-	{#State 113
+	{#State 115
 		ACTIONS => {
-			"\$" => 42,
-			'IDENT' => 203,
-			"\${" => 37
+			"\$" => 44,
+			'IDENT' => 206,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 174
+			'ident' => 177
 		}
 	},
-	{#State 114
+	{#State 116
 		ACTIONS => {
-			";" => 204
+			";" => 207
 		}
 	},
-	{#State 115
+	{#State 117
 		ACTIONS => {
-			'ASSIGN' => -160
+			'ASSIGN' => -162
 		},
-		DEFAULT => -168
+		DEFAULT => -170
 	},
-	{#State 116
-		DEFAULT => -175,
+	{#State 118
+		DEFAULT => -177,
 		GOTOS => {
-			'quoted' => 205
+			'quoted' => 208
 		}
 	},
-	{#State 117
-		DEFAULT => -157
-	},
-	{#State 118
-		ACTIONS => {
-			'DOT' => 103
-		},
-		DEFAULT => -109
-	},
 	{#State 119
-		ACTIONS => {
-			'ASSIGN' => 185
-		},
-		DEFAULT => -172
+		DEFAULT => -159
 	},
 	{#State 120
-		DEFAULT => -82
-	},
-	{#State 121
 		ACTIONS => {
-			'DOT' => 173
+			'DOT' => 105
 		},
-		DEFAULT => -83
+		DEFAULT => -111
+	},
+	{#State 121
+		DEFAULT => -76
 	},
 	{#State 122
 		ACTIONS => {
-			";" => 206
-		}
+			'ASSIGN' => 188
+		},
+		DEFAULT => -174
 	},
 	{#State 123
 		DEFAULT => -84
 	},
 	{#State 124
 		ACTIONS => {
-			"}" => 207
-		}
+			'DOT' => 176
+		},
+		DEFAULT => -85
 	},
 	{#State 125
 		ACTIONS => {
-			'ASSIGN' => 208
+			";" => 209
 		}
 	},
 	{#State 126
-		DEFAULT => -121
+		DEFAULT => -86
 	},
 	{#State 127
 		ACTIONS => {
-			"\$" => 42,
-			'COMMA' => 210,
-			'LITERAL' => 128,
-			'IDENT' => 2,
-			"\${" => 37
-		},
-		DEFAULT => -117,
-		GOTOS => {
-			'item' => 125,
-			'param' => 209
+			"}" => 210
 		}
 	},
 	{#State 128
@@ -12209,3618 +12360,3715 @@ $STATES = [
 		}
 	},
 	{#State 129
-		DEFAULT => -74
+		DEFAULT => -123
 	},
 	{#State 130
-		DEFAULT => -4
+		ACTIONS => {
+			"\$" => 44,
+			'COMMA' => 213,
+			'LITERAL' => 131,
+			'IDENT' => 2,
+			"\${" => 38
+		},
+		DEFAULT => -119,
+		GOTOS => {
+			'item' => 128,
+			'param' => 212
+		}
 	},
 	{#State 131
 		ACTIONS => {
-			";" => 212
+			'ASSIGN' => 214
 		}
 	},
 	{#State 132
+		DEFAULT => -75
+	},
+	{#State 133
+		DEFAULT => -4
+	},
+	{#State 134
 		ACTIONS => {
-			"}" => 213
+			";" => 215
 		}
 	},
-	{#State 133
+	{#State 135
+		ACTIONS => {
+			"}" => 216
+		}
+	},
+	{#State 136
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -141
+		DEFAULT => -143
 	},
-	{#State 134
-		DEFAULT => -155,
+	{#State 137
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 214
+			'args' => 217
 		}
 	},
-	{#State 135
-		DEFAULT => -131
+	{#State 138
+		DEFAULT => -133
 	},
-	{#State 136
+	{#State 139
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 215,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 218,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 137
+	{#State 140
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -30
+		DEFAULT => -31
 	},
-	{#State 138
+	{#State 141
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -29
+		DEFAULT => -30
 	},
-	{#State 139
+	{#State 142
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 216,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 219,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 140
+	{#State 143
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
-			'IDENT' => 107,
-			"\"" => 60,
-			"\$" => 42,
+			"{" => 31,
+			'LITERAL' => 80,
+			'IDENT' => 109,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
-			'loopvar' => 217,
+			'sterm' => 70,
+			'item' => 41,
+			'loopvar' => 220,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 108,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 110,
+			'lterm' => 58
 		}
 	},
-	{#State 141
+	{#State 144
 		ACTIONS => {
-			"\"" => 116,
-			"\$" => 113,
-			'LITERAL' => 115,
-			'FILENAME' => 83,
-			'IDENT' => 110,
-			'NUMBER' => 84,
-			"\${" => 37
-		},
-		GOTOS => {
-			'names' => 91,
-			'lvalue' => 111,
-			'item' => 112,
-			'name' => 82,
-			'filepart' => 87,
-			'filename' => 85,
-			'nameargs' => 117,
-			'lnameargs' => 218
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
+		},
+		GOTOS => {
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 221
 		}
 	},
-	{#State 142
+	{#State 145
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 219,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 222,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 143
+	{#State 146
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 220,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 223,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 144
+	{#State 147
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 221,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 224,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 145
-		DEFAULT => -42
+	{#State 148
+		DEFAULT => -43
 	},
-	{#State 146
+	{#State 149
 		DEFAULT => 0
 	},
-	{#State 147
+	{#State 150
 		ACTIONS => {
-			'DOT' => 103,
-			'ASSIGN' => 171
+			'DOT' => 105,
+			'ASSIGN' => 174
 		},
-		DEFAULT => -108
+		DEFAULT => -110
 	},
-	{#State 148
+	{#State 151
 		ACTIONS => {
-			")" => 222
+			")" => 225
 		}
 	},
-	{#State 149
+	{#State 152
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			")" => 223,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			")" => 226,
+			'OR' => 164
 		}
 	},
-	{#State 150
+	{#State 153
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 224,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 227,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 151
-		DEFAULT => -77,
+	{#State 154
+		DEFAULT => -79,
 		GOTOS => {
-			'@4-2' => 225
+			'@4-2' => 228
 		}
 	},
-	{#State 152
+	{#State 155
 		ACTIONS => {
-			";" => 226
+			";" => 229
 		}
 	},
-	{#State 153
+	{#State 156
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 227,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 230,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 154
+	{#State 157
 		ACTIONS => {
-			"\"" => 232,
-			'TEXT' => 229,
-			";" => 231,
-			"\$" => 42,
+			"\"" => 235,
+			'TEXT' => 232,
+			";" => 234,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 228,
-			'quotable' => 230
+			'ident' => 231,
+			'quotable' => 233
 		}
 	},
-	{#State 155
-		DEFAULT => -35
+	{#State 158
+		DEFAULT => -36
 	},
-	{#State 156
+	{#State 159
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 233,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 236,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 157
+	{#State 160
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 234,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 237,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 158
+	{#State 161
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 235,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 238,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 159
+	{#State 162
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 236,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 239,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 160
+	{#State 163
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 237,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 240,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 161
+	{#State 164
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 238,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 241,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 162
+	{#State 165
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 239,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 242,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 163
+	{#State 166
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 240,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 243,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 164
+	{#State 167
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 241,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 244,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 165
+	{#State 168
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 242,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 245,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 166
-		DEFAULT => -33
+	{#State 169
+		DEFAULT => -34
 	},
-	{#State 167
+	{#State 170
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 243,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 246,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 168
+	{#State 171
 		ACTIONS => {
-			'COMMA' => 170,
-			'LITERAL' => 75,
+			'COMMA' => 173,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\$" => 42,
-			"\${" => 37
+			"\$" => 44,
+			"\${" => 38
 		},
-		DEFAULT => -32,
+		DEFAULT => -33,
 		GOTOS => {
-			'item' => 39,
-			'assign' => 169,
+			'item' => 41,
+			'assign' => 172,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 169
-		DEFAULT => -146
+	{#State 172
+		DEFAULT => -148
 	},
-	{#State 170
-		DEFAULT => -147
+	{#State 173
+		DEFAULT => -149
 	},
-	{#State 171
+	{#State 174
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 244,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 247,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 172
+	{#State 175
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 245,
-			'directive' => 71,
+			'block' => 248,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 173
+	{#State 176
 		ACTIONS => {
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 246
+			'filepart' => 249
 		}
 	},
-	{#State 174
+	{#State 177
 		ACTIONS => {
-			'DOT' => 103
+			'DOT' => 105
 		},
-		DEFAULT => -155,
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 247
+			'args' => 250
 		}
 	},
-	{#State 175
+	{#State 178
 		ACTIONS => {
-			"\"" => 248,
-			'TEXT' => 229,
-			";" => 231,
-			"\$" => 42,
+			"\"" => 251,
+			'TEXT' => 232,
+			";" => 234,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 228,
-			'quotable' => 230
+			'ident' => 231,
+			'quotable' => 233
 		}
 	},
-	{#State 176
+	{#State 179
 		ACTIONS => {
-			"\"" => 89,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'filename' => 85,
-			'name' => 249
+			'filepart' => 89,
+			'filename' => 87,
+			'name' => 252
 		}
 	},
-	{#State 177
-		DEFAULT => -155,
+	{#State 180
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 250
+			'args' => 253
 		}
 	},
-	{#State 178
+	{#State 181
 		ACTIONS => {
-			'LITERAL' => 254,
+			'NOT' => 40,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"{" => 30,
-			'COMMA' => 256,
-			"\${" => 37
+			"{" => 31,
+			'COMMA' => 259,
+			"(" => 54,
+			"\${" => 38
 		},
-		DEFAULT => -162,
+		DEFAULT => -164,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 179
-		DEFAULT => -104
+	{#State 182
+		DEFAULT => -106
 	},
-	{#State 180
-		DEFAULT => -113
+	{#State 183
+		DEFAULT => -115
 	},
-	{#State 181
-		DEFAULT => -114
+	{#State 184
+		DEFAULT => -116
 	},
-	{#State 182
-		DEFAULT => -105
+	{#State 185
+		DEFAULT => -107
 	},
-	{#State 183
+	{#State 186
 		ACTIONS => {
-			"\"" => 60,
-			"\$" => 42,
-			'LITERAL' => 78,
+			"\"" => 62,
+			"\$" => 44,
+			'LITERAL' => 80,
 			'IDENT' => 2,
 			'REF' => 27,
 			'NUMBER' => 26,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 257,
-			'item' => 39,
+			'sterm' => 260,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77
+			'ident' => 79
 		}
 	},
-	{#State 184
+	{#State 187
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 259
+			'final' => 262
 		}
 	},
-	{#State 185
+	{#State 188
 		ACTIONS => {
-			"\"" => 263,
-			'LITERAL' => 262,
-			'NUMBER' => 261
+			"\"" => 266,
+			'LITERAL' => 265,
+			'NUMBER' => 264
 		}
 	},
-	{#State 186
-		DEFAULT => -96
+	{#State 189
+		DEFAULT => -98
 	},
-	{#State 187
-		DEFAULT => -97
+	{#State 190
+		DEFAULT => -99
 	},
-	{#State 188
+	{#State 191
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
-			'node' => 23,
-			'term' => 58,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'template' => 264,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'template' => 267,
 			'defblockname' => 14,
-			'filter' => 29,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
 			'assign' => 19,
-			'block' => 72,
-			'directive' => 71,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'lterm' => 58,
+			'node' => 23,
+			'term' => 60,
+			'expr' => 64,
+			'use' => 65,
+			'defblock' => 68,
+			'filter' => 30,
+			'sterm' => 70,
+			'chunks' => 34,
+			'setlist' => 72,
+			'switch' => 35,
+			'try' => 36,
+			'directive' => 73,
+			'block' => 74,
+			'raw' => 39,
+			'condition' => 75
 		}
 	},
-	{#State 189
-		DEFAULT => -124
+	{#State 192
+		DEFAULT => -126
 	},
-	{#State 190
-		DEFAULT => -125
+	{#State 193
+		DEFAULT => -127
 	},
-	{#State 191
+	{#State 194
 		ACTIONS => {
-			";" => 265
+			";" => 268
 		}
 	},
-	{#State 192
-		DEFAULT => -88
+	{#State 195
+		DEFAULT => -90
 	},
-	{#State 193
+	{#State 196
 		ACTIONS => {
-			";" => -149,
-			"+" => 156,
-			'LITERAL' => -149,
-			'IDENT' => -149,
-			'CAT' => 162,
-			"\$" => -149,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			'COMMA' => -149,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161,
-			"\${" => -149
+			";" => -151,
+			"+" => 159,
+			'LITERAL' => -151,
+			'IDENT' => -151,
+			'CAT' => 165,
+			"\$" => -151,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			'COMMA' => -151,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"\${" => -151
 		},
-		DEFAULT => -27
+		DEFAULT => -28
 	},
-	{#State 194
-		DEFAULT => -91
+	{#State 197
+		DEFAULT => -93
 	},
-	{#State 195
-		DEFAULT => -90
+	{#State 198
+		DEFAULT => -92
 	},
-	{#State 196
+	{#State 199
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 57,
-			'IDENT' => 266,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 59,
+			'IDENT' => 269,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 149,
-			'sterm' => 68,
-			'item' => 39,
-			'assign' => 148,
-			'margs' => 267,
+			'expr' => 152,
+			'sterm' => 70,
+			'item' => 41,
+			'assign' => 151,
+			'margs' => 270,
 			'node' => 23,
-			'ident' => 147,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 150,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 197
+	{#State 200
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -27
+		DEFAULT => -28
 	},
-	{#State 198
+	{#State 201
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 268,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 271,
+			'lterm' => 58
 		}
 	},
-	{#State 199
+	{#State 202
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 269,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 272,
+			'lterm' => 58
 		}
 	},
-	{#State 200
+	{#State 203
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -65,
+		DEFAULT => -66,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 201
-		DEFAULT => -57,
+	{#State 204
+		DEFAULT => -58,
 		GOTOS => {
-			'@1-3' => 270
+			'@1-3' => 273
 		}
 	},
-	{#State 202
+	{#State 205
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 271,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 274,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 203
+	{#State 206
 		ACTIONS => {
-			'ASSIGN' => -131
+			'ASSIGN' => -133
 		},
-		DEFAULT => -129
+		DEFAULT => -131
 	},
-	{#State 204
+	{#State 207
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 272,
-			'directive' => 71,
+			'block' => 275,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 205
+	{#State 208
 		ACTIONS => {
-			"\"" => 273,
-			'TEXT' => 229,
-			";" => 231,
-			"\$" => 42,
+			"\"" => 276,
+			'TEXT' => 232,
+			";" => 234,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 228,
-			'quotable' => 230
+			'ident' => 231,
+			'quotable' => 233
 		}
 	},
-	{#State 206
+	{#State 209
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 274,
-			'directive' => 71,
+			'block' => 277,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 207
-		DEFAULT => -107
+	{#State 210
+		DEFAULT => -109
 	},
-	{#State 208
+	{#State 211
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 275,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 278,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 209
-		DEFAULT => -119
+	{#State 212
+		DEFAULT => -121
 	},
-	{#State 210
-		DEFAULT => -120
+	{#State 213
+		DEFAULT => -122
 	},
-	{#State 211
+	{#State 214
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 276,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 279,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 212
-		DEFAULT => -75,
+	{#State 215
+		DEFAULT => -77,
 		GOTOS => {
-			'@3-3' => 277
+			'@3-3' => 280
 		}
 	},
-	{#State 213
-		DEFAULT => -130
+	{#State 216
+		DEFAULT => -132
 	},
-	{#State 214
+	{#State 217
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			")" => 278,
-			"\${" => 37
+			")" => 281,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 215
+	{#State 218
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 279,
-			'directive' => 71,
+			'block' => 282,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 216
+	{#State 219
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -48
+		DEFAULT => -49
 	},
-	{#State 217
-		DEFAULT => -59
+	{#State 220
+		DEFAULT => -60
 	},
-	{#State 218
-		DEFAULT => -80
+	{#State 221
+		DEFAULT => -82
 	},
-	{#State 219
+	{#State 222
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -46
+		DEFAULT => -47
 	},
-	{#State 220
-		DEFAULT => -67
+	{#State 223
+		DEFAULT => -68
 	},
-	{#State 221
+	{#State 224
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -62
+		DEFAULT => -63
 	},
-	{#State 222
-		DEFAULT => -143
+	{#State 225
+		DEFAULT => -145
 	},
-	{#State 223
-		DEFAULT => -144
+	{#State 226
+		DEFAULT => -146
 	},
-	{#State 224
+	{#State 227
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 280,
-			'directive' => 71,
+			'block' => 283,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 225
+	{#State 228
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 281,
-			'directive' => 71,
+			'block' => 284,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 226
+	{#State 229
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 282,
-			'directive' => 71,
+			'block' => 285,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 227
+	{#State 230
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -150
+		DEFAULT => -152
 	},
-	{#State 228
+	{#State 231
 		ACTIONS => {
-			'DOT' => 103
+			'DOT' => 105
 		},
-		DEFAULT => -176
-	},
-	{#State 229
-		DEFAULT => -177
-	},
-	{#State 230
-		DEFAULT => -174
-	},
-	{#State 231
 		DEFAULT => -178
 	},
 	{#State 232
-		DEFAULT => -110
+		DEFAULT => -179
 	},
 	{#State 233
-		ACTIONS => {
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165
-		},
-		DEFAULT => -134
+		DEFAULT => -176
 	},
 	{#State 234
-		ACTIONS => {
-			":" => 283,
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
-		}
+		DEFAULT => -180
 	},
 	{#State 235
-		ACTIONS => {
-			'MOD' => 164
-		},
-		DEFAULT => -135
+		DEFAULT => -112
 	},
 	{#State 236
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -139
+		DEFAULT => -136
 	},
 	{#State 237
 		ACTIONS => {
-			"+" => 156,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165
-		},
-		DEFAULT => -132
+			":" => 286,
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
+		}
 	},
 	{#State 238
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'MOD' => 167
 		},
-		DEFAULT => -140
+		DEFAULT => -137
 	},
 	{#State 239
 		ACTIONS => {
-			"+" => 156,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -138
+		DEFAULT => -141
 	},
 	{#State 240
 		ACTIONS => {
-			"+" => 156,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			"+" => 159,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -137
+		DEFAULT => -134
 	},
 	{#State 241
-		DEFAULT => -136
+		ACTIONS => {
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
+		},
+		DEFAULT => -142
 	},
 	{#State 242
 		ACTIONS => {
-			'DIV' => 158,
-			'MOD' => 164
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -133
+		DEFAULT => -140
 	},
 	{#State 243
-		DEFAULT => -60,
+		ACTIONS => {
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'MOD' => 167,
+			"/" => 168
+		},
+		DEFAULT => -139
+	},
+	{#State 244
+		DEFAULT => -138
+	},
+	{#State 245
+		ACTIONS => {
+			'DIV' => 161,
+			'MOD' => 167
+		},
+		DEFAULT => -135
+	},
+	{#State 246
+		DEFAULT => -61,
 		GOTOS => {
-			'@2-3' => 284
+			'@2-3' => 287
 		}
 	},
-	{#State 244
+	{#State 247
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -149
+		DEFAULT => -151
 	},
-	{#State 245
+	{#State 248
 		ACTIONS => {
-			'ELSIF' => 287,
-			'ELSE' => 285
+			'ELSIF' => 290,
+			'ELSE' => 288
 		},
-		DEFAULT => -51,
+		DEFAULT => -52,
 		GOTOS => {
-			'else' => 286
+			'else' => 289
 		}
 	},
-	{#State 246
-		DEFAULT => -169
+	{#State 249
+		DEFAULT => -171
 	},
-	{#State 247
+	{#State 250
 		ACTIONS => {
-			'LITERAL' => 254,
+			'NOT' => 40,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"{" => 30,
-			'COMMA' => 256,
-			"\${" => 37
+			"{" => 31,
+			'COMMA' => 259,
+			"(" => 54,
+			"\${" => 38
 		},
-		DEFAULT => -161,
+		DEFAULT => -163,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 248
-		DEFAULT => -166
+	{#State 251
+		DEFAULT => -168
 	},
-	{#State 249
-		DEFAULT => -164
+	{#State 252
+		DEFAULT => -166
 	},
-	{#State 250
+	{#State 253
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			")" => 288,
-			"\${" => 37
+			")" => 291,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 251
-		ACTIONS => {
-			'DOT' => 103,
-			'ASSIGN' => 289
-		},
-		DEFAULT => -108
-	},
-	{#State 252
+	{#State 254
 		ACTIONS => {
-			"(" => 134,
-			'ASSIGN' => 208
+			'DOT' => 105,
+			'ASSIGN' => 292
 		},
-		DEFAULT => -127
-	},
-	{#State 253
-		DEFAULT => -152
+		DEFAULT => -110
 	},
-	{#State 254
+	{#State 255
 		ACTIONS => {
+			"(" => 137,
 			'ASSIGN' => 211
 		},
-		DEFAULT => -111
-	},
-	{#State 255
-		DEFAULT => -151
+		DEFAULT => -129
 	},
 	{#State 256
 		DEFAULT => -154
 	},
 	{#State 257
-		DEFAULT => -116
+		ACTIONS => {
+			'ASSIGN' => 214
+		},
+		DEFAULT => -113
 	},
 	{#State 258
 		ACTIONS => {
-			";" => 290
-		}
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
+		},
+		DEFAULT => -153
 	},
 	{#State 259
+		DEFAULT => -156
+	},
+	{#State 260
+		DEFAULT => -118
+	},
+	{#State 261
 		ACTIONS => {
-			'END' => 291
+			";" => 293
 		}
 	},
-	{#State 260
+	{#State 262
 		ACTIONS => {
-			";" => 293,
-			'DEFAULT' => 294,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			'END' => 294
+		}
+	},
+	{#State 263
+		ACTIONS => {
+			";" => 296,
+			'DEFAULT' => 297,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'filename' => 292
+			'filepart' => 89,
+			'filename' => 295
 		}
 	},
-	{#State 261
-		DEFAULT => -101
+	{#State 264
+		DEFAULT => -103
 	},
-	{#State 262
-		DEFAULT => -99
+	{#State 265
+		DEFAULT => -101
 	},
-	{#State 263
+	{#State 266
 		ACTIONS => {
-			'TEXT' => 295
+			'TEXT' => 298
 		}
 	},
-	{#State 264
+	{#State 267
 		ACTIONS => {
-			'END' => 296
+			'END' => 299
 		}
 	},
-	{#State 265
+	{#State 268
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 297,
-			'directive' => 71,
+			'block' => 300,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 266
+	{#State 269
 		ACTIONS => {
-			'IDENT' => -95,
-			")" => -95,
-			'COMMA' => -95
+			'IDENT' => -97,
+			")" => -97,
+			'COMMA' => -97
 		},
-		DEFAULT => -129
+		DEFAULT => -131
 	},
-	{#State 267
+	{#State 270
 		ACTIONS => {
-			'COMMA' => 300,
-			'IDENT' => 298,
-			")" => 299
+			'COMMA' => 303,
+			'IDENT' => 301,
+			")" => 302
 		}
 	},
-	{#State 268
-		DEFAULT => -155,
+	{#State 271
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 301
+			'args' => 304
 		}
 	},
-	{#State 269
-		DEFAULT => -155,
+	{#State 272
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 302
+			'args' => 305
 		}
 	},
-	{#State 270
+	{#State 273
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
-			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
-			'FOR' => 21,
-			'LITERAL' => 57,
-			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
-			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
-			'FILTER' => 25,
-			'INSERT' => 65,
-			'NUMBER' => 26,
-			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			"(" => 54,
+			'SWITCH' => 55,
+			'MACRO' => 18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
+			'FOR' => 21,
+			'LITERAL' => 59,
+			'NEXT' => 22,
+			'rawperl' => 61,
+			"\"" => 62,
+			'TEXT' => 24,
+			'PROCESS' => 63,
+			'RETURN' => 66,
+			'FILTER' => 25,
+			'INSERT' => 67,
+			'NUMBER' => 26,
+			'REF' => 27,
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 303,
-			'directive' => 71,
+			'block' => 306,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 271
-		DEFAULT => -156
+	{#State 274
+		DEFAULT => -158
 	},
-	{#State 272
+	{#State 275
 		ACTIONS => {
-			'END' => 304
+			'END' => 307
 		}
 	},
-	{#State 273
+	{#State 276
 		ACTIONS => {
-			'ASSIGN' => -159
+			'ASSIGN' => -161
 		},
-		DEFAULT => -166
+		DEFAULT => -168
 	},
-	{#State 274
+	{#State 277
 		ACTIONS => {
-			'END' => 305
+			'END' => 308
 		}
 	},
-	{#State 275
+	{#State 278
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'DIV' => 161,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -123
+		DEFAULT => -125
 	},
-	{#State 276
+	{#State 279
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'DIV' => 161,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -122
+		DEFAULT => -124
 	},
-	{#State 277
+	{#State 280
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 306,
-			'directive' => 71,
+			'block' => 309,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 278
-		DEFAULT => -128
+	{#State 281
+		DEFAULT => -130
 	},
-	{#State 279
+	{#State 282
 		ACTIONS => {
-			'ELSIF' => 287,
-			'ELSE' => 285
+			'ELSIF' => 290,
+			'ELSE' => 288
 		},
-		DEFAULT => -51,
+		DEFAULT => -52,
 		GOTOS => {
-			'else' => 307
+			'else' => 310
 		}
 	},
-	{#State 280
+	{#State 283
 		ACTIONS => {
-			'CASE' => 308
+			'CASE' => 311
 		},
-		DEFAULT => -56,
+		DEFAULT => -57,
 		GOTOS => {
-			'case' => 309
+			'case' => 312
 		}
 	},
-	{#State 281
+	{#State 284
 		ACTIONS => {
-			'END' => 310
+			'END' => 313
 		}
 	},
-	{#State 282
+	{#State 285
 		ACTIONS => {
-			'END' => 311
+			'END' => 314
 		}
 	},
-	{#State 283
+	{#State 286
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 312,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 315,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 284
+	{#State 287
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 313,
-			'directive' => 71,
+			'block' => 316,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 285
+	{#State 288
 		ACTIONS => {
-			";" => 314
+			";" => 317
 		}
 	},
-	{#State 286
+	{#State 289
 		ACTIONS => {
-			'END' => 315
+			'END' => 318
 		}
 	},
-	{#State 287
+	{#State 290
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 316,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 319,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 288
-		DEFAULT => -163
+	{#State 291
+		DEFAULT => -165
 	},
-	{#State 289
+	{#State 292
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 317,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 320,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 290
+	{#State 293
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 318,
-			'directive' => 71,
+			'block' => 321,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 291
-		DEFAULT => -68
+	{#State 294
+		DEFAULT => -69
 	},
-	{#State 292
+	{#State 295
 		ACTIONS => {
-			'DOT' => 173,
-			";" => 319
+			'DOT' => 176,
+			";" => 322
 		}
 	},
-	{#State 293
+	{#State 296
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 320,
-			'directive' => 71,
+			'block' => 323,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 294
+	{#State 297
 		ACTIONS => {
-			";" => 321
+			";" => 324
 		}
 	},
-	{#State 295
+	{#State 298
 		ACTIONS => {
-			"\"" => 322
+			"\"" => 325
 		}
 	},
-	{#State 296
-		DEFAULT => -81
+	{#State 299
+		DEFAULT => -83
 	},
-	{#State 297
+	{#State 300
 		ACTIONS => {
-			'END' => 323
+			'END' => 326
 		}
 	},
-	{#State 298
-		DEFAULT => -93
+	{#State 301
+		DEFAULT => -95
 	},
-	{#State 299
+	{#State 302
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
+			"(" => 54,
+			'SWITCH' => 55,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
 			'FOR' => 21,
 			'NEXT' => 22,
-			'LITERAL' => 57,
-			"\"" => 60,
-			'PROCESS' => 61,
+			'LITERAL' => 59,
+			"\"" => 62,
+			'PROCESS' => 63,
 			'FILTER' => 25,
-			'RETURN' => 64,
-			'INSERT' => 65,
+			'RETURN' => 66,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 191,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			"\${" => 37
+			'WHILE' => 69,
+			'BLOCK' => 194,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'expr' => 197,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'expr' => 200,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'atomdir' => 13,
-			'mdir' => 324,
-			'filter' => 29,
-			'sterm' => 68,
-			'ident' => 147,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'mdir' => 327,
+			'filter' => 30,
+			'sterm' => 70,
+			'ident' => 150,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 194,
-			'condition' => 73,
-			'lterm' => 56
+			'directive' => 197,
+			'condition' => 75,
+			'lterm' => 58
 		}
 	},
-	{#State 300
-		DEFAULT => -94
+	{#State 303
+		DEFAULT => -96
 	},
-	{#State 301
+	{#State 304
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -63,
+		DEFAULT => -64,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 302
+	{#State 305
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -64,
+		DEFAULT => -65,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 303
+	{#State 306
 		ACTIONS => {
-			'END' => 325
+			'END' => 328
 		}
 	},
-	{#State 304
-		DEFAULT => -79
+	{#State 307
+		DEFAULT => -81
 	},
-	{#State 305
-		DEFAULT => -87
+	{#State 308
+		DEFAULT => -89
 	},
-	{#State 306
+	{#State 309
 		ACTIONS => {
-			'END' => 326
+			'END' => 329
 		}
 	},
-	{#State 307
+	{#State 310
 		ACTIONS => {
-			'END' => 327
+			'END' => 330
 		}
 	},
-	{#State 308
+	{#State 311
 		ACTIONS => {
-			";" => 328,
-			'DEFAULT' => 330,
-			"{" => 30,
-			'LITERAL' => 78,
+			";" => 331,
+			'DEFAULT' => 333,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 329,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 332,
+			'lterm' => 58
 		}
 	},
-	{#State 309
+	{#State 312
 		ACTIONS => {
-			'END' => 331
+			'END' => 334
 		}
 	},
-	{#State 310
-		DEFAULT => -78
+	{#State 313
+		DEFAULT => -80
 	},
-	{#State 311
-		DEFAULT => -66
+	{#State 314
+		DEFAULT => -67
 	},
-	{#State 312
+	{#State 315
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'DIV' => 161,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -142
+		DEFAULT => -144
 	},
-	{#State 313
+	{#State 316
 		ACTIONS => {
-			'END' => 332
+			'END' => 335
 		}
 	},
-	{#State 314
+	{#State 317
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 333,
-			'directive' => 71,
+			'block' => 336,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 315
-		DEFAULT => -47
+	{#State 318
+		DEFAULT => -48
 	},
-	{#State 316
+	{#State 319
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 334,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 337,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 317
+	{#State 320
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -153
+		DEFAULT => -155
 	},
-	{#State 318
-		DEFAULT => -72
+	{#State 321
+		DEFAULT => -73
 	},
-	{#State 319
+	{#State 322
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 335,
-			'directive' => 71,
+			'block' => 338,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 320
+	{#State 323
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 336
+			'final' => 339
 		}
 	},
-	{#State 321
+	{#State 324
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 337,
-			'directive' => 71,
+			'block' => 340,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 322
-		DEFAULT => -100
-	},
-	{#State 323
-		DEFAULT => -92
-	},
-	{#State 324
-		DEFAULT => -89
-	},
 	{#State 325
-		DEFAULT => -58
+		DEFAULT => -102
 	},
 	{#State 326
-		DEFAULT => -76
+		DEFAULT => -94
 	},
 	{#State 327
-		DEFAULT => -45
+		DEFAULT => -91
 	},
 	{#State 328
+		DEFAULT => -59
+	},
+	{#State 329
+		DEFAULT => -78
+	},
+	{#State 330
+		DEFAULT => -46
+	},
+	{#State 331
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
-			'NUMBER' => 26,
-			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'INSERT' => 67,
+			'NUMBER' => 26,
+			'REF' => 27,
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 338,
-			'directive' => 71,
+			'block' => 341,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 329
+	{#State 332
 		ACTIONS => {
-			";" => 339
+			";" => 342
 		}
 	},
-	{#State 330
+	{#State 333
 		ACTIONS => {
-			";" => 340
+			";" => 343
 		}
 	},
-	{#State 331
-		DEFAULT => -52
+	{#State 334
+		DEFAULT => -53
 	},
-	{#State 332
-		DEFAULT => -61
+	{#State 335
+		DEFAULT => -62
 	},
-	{#State 333
-		DEFAULT => -50
+	{#State 336
+		DEFAULT => -51
 	},
-	{#State 334
+	{#State 337
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 341,
-			'directive' => 71,
+			'block' => 344,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 335
+	{#State 338
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 342
+			'final' => 345
 		}
 	},
-	{#State 336
-		DEFAULT => -71
+	{#State 339
+		DEFAULT => -72
 	},
-	{#State 337
+	{#State 340
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 343
+			'final' => 346
 		}
 	},
-	{#State 338
-		DEFAULT => -55
+	{#State 341
+		DEFAULT => -56
 	},
-	{#State 339
+	{#State 342
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 344,
-			'directive' => 71,
+			'block' => 347,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 340
+	{#State 343
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 345,
-			'directive' => 71,
+			'block' => 348,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 341
+	{#State 344
 		ACTIONS => {
-			'ELSIF' => 287,
-			'ELSE' => 285
+			'ELSIF' => 290,
+			'ELSE' => 288
 		},
-		DEFAULT => -51,
+		DEFAULT => -52,
 		GOTOS => {
-			'else' => 346
+			'else' => 349
 		}
 	},
-	{#State 342
-		DEFAULT => -69
-	},
-	{#State 343
+	{#State 345
 		DEFAULT => -70
 	},
-	{#State 344
+	{#State 346
+		DEFAULT => -71
+	},
+	{#State 347
 		ACTIONS => {
-			'CASE' => 308
+			'CASE' => 311
 		},
-		DEFAULT => -56,
+		DEFAULT => -57,
 		GOTOS => {
-			'case' => 347
+			'case' => 350
 		}
 	},
-	{#State 345
-		DEFAULT => -54
+	{#State 348
+		DEFAULT => -55
 	},
-	{#State 346
-		DEFAULT => -49
+	{#State 349
+		DEFAULT => -50
 	},
-	{#State 347
-		DEFAULT => -53
+	{#State 350
+		DEFAULT => -54
 	}
 ]; 
 
@@ -15893,26 +16141,26 @@ sub
 		 'statement', 1, undef
 	],
 	[#Rule 16
+		 'statement', 1, undef
+	],
+	[#Rule 17
 		 'statement', 1,
 sub
 { $factory->get($_[1])                }
 	],
-	[#Rule 17
+	[#Rule 18
 		 'statement', 2,
 sub
 { $_[0]->add_metadata($_[2]);         }
 	],
-	[#Rule 18
+	[#Rule 19
 		 'statement', 0, undef
 	],
-	[#Rule 19
+	[#Rule 20
 		 'directive', 1,
 sub
 { $factory->set($_[1])                }
 	],
-	[#Rule 20
-		 'directive', 1, undef
-	],
 	[#Rule 21
 		 'directive', 1, undef
 	],
@@ -15932,79 +16180,82 @@ sub
 		 'directive', 1, undef
 	],
 	[#Rule 27
+		 'directive', 1, undef
+	],
+	[#Rule 28
 		 'atomexpr', 1,
 sub
 { $factory->get($_[1])                }
 	],
-	[#Rule 28
+	[#Rule 29
 		 'atomexpr', 1, undef
 	],
-	[#Rule 29
+	[#Rule 30
 		 'atomdir', 2,
 sub
 { $factory->get($_[2])                }
 	],
-	[#Rule 30
+	[#Rule 31
 		 'atomdir', 2,
 sub
 { $factory->call($_[2])               }
 	],
-	[#Rule 31
+	[#Rule 32
 		 'atomdir', 2,
 sub
 { $factory->set($_[2])                }
 	],
-	[#Rule 32
+	[#Rule 33
 		 'atomdir', 2,
 sub
 { $factory->default($_[2])            }
 	],
-	[#Rule 33
+	[#Rule 34
 		 'atomdir', 2,
 sub
 { $factory->insert($_[2])             }
 	],
-	[#Rule 34
+	[#Rule 35
 		 'atomdir', 2,
 sub
 { $factory->include($_[2])            }
 	],
-	[#Rule 35
+	[#Rule 36
 		 'atomdir', 2,
 sub
 { $factory->process($_[2])            }
 	],
-	[#Rule 36
+	[#Rule 37
 		 'atomdir', 2,
 sub
 { $factory->throw($_[2])              }
 	],
-	[#Rule 37
+	[#Rule 38
 		 'atomdir', 1,
 sub
 { $factory->return()                  }
 	],
-	[#Rule 38
+	[#Rule 39
 		 'atomdir', 1,
 sub
 { $factory->stop()                    }
 	],
-	[#Rule 39
+	[#Rule 40
 		 'atomdir', 1,
 sub
 { $factory->clear()                   }
 	],
-	[#Rule 40
+	[#Rule 41
 		 'atomdir', 1,
 sub
 { $factory->break()                   }
 	],
-	[#Rule 41
+	[#Rule 42
 		 'atomdir', 1,
 sub
 { $factory->next()                    }
 	],
-	[#Rule 42
+	[#Rule 43
 		 'atomdir', 2,
 sub
 { if ($_[2]->[0]->[0] =~ /^'(on|off)'$/) {
@@ -16016,186 +16267,191 @@ sub
 				      }
 				    }
 	],
-	[#Rule 43
-		 'atomdir', 1, undef
-	],
 	[#Rule 44
 		 'atomdir', 1, undef
 	],
 	[#Rule 45
+		 'atomdir', 1, undef
+	],
+	[#Rule 46
 		 'condition', 6,
 sub
 { $factory->if(@_[2, 4, 5])           }
 	],
-	[#Rule 46
+	[#Rule 47
 		 'condition', 3,
 sub
 { $factory->if(@_[3, 1])              }
 	],
-	[#Rule 47
+	[#Rule 48
 		 'condition', 6,
 sub
 { $factory->if("!($_[2])", @_[4, 5])  }
 	],
-	[#Rule 48
+	[#Rule 49
 		 'condition', 3,
 sub
 { $factory->if("!($_[3])", $_[1])     }
 	],
-	[#Rule 49
+	[#Rule 50
 		 'else', 5,
 sub
 { unshift(@{$_[5]}, [ @_[2, 4] ]);
 				      $_[5];                              }
 	],
-	[#Rule 50
+	[#Rule 51
 		 'else', 3,
 sub
 { [ $_[3] ]                           }
 	],
-	[#Rule 51
+	[#Rule 52
 		 'else', 0,
 sub
 { [ undef ]                           }
 	],
-	[#Rule 52
+	[#Rule 53
 		 'switch', 6,
 sub
 { $factory->switch(@_[2, 5])          }
 	],
-	[#Rule 53
+	[#Rule 54
 		 'case', 5,
 sub
 { unshift(@{$_[5]}, [ @_[2, 4] ]); 
 				      $_[5];                              }
 	],
-	[#Rule 54
+	[#Rule 55
 		 'case', 4,
 sub
 { [ $_[4] ]                           }
 	],
-	[#Rule 55
+	[#Rule 56
 		 'case', 3,
 sub
 { [ $_[3] ]                           }
 	],
-	[#Rule 56
+	[#Rule 57
 		 'case', 0,
 sub
 { [ undef ]                           }
 	],
-	[#Rule 57
+	[#Rule 58
 		 '@1-3', 0,
 sub
 { $_[0]->{ INFOR }++                  }
 	],
-	[#Rule 58
+	[#Rule 59
 		 'loop', 6,
 sub
 { $_[0]->{ INFOR }--;
 				      $factory->foreach(@{$_[2]}, $_[5])  }
 	],
-	[#Rule 59
+	[#Rule 60
 		 'loop', 3,
 sub
 { $factory->foreach(@{$_[3]}, $_[1])  }
 	],
-	[#Rule 60
+	[#Rule 61
 		 '@2-3', 0,
 sub
 { $_[0]->{ INWHILE }++                }
 	],
-	[#Rule 61
+	[#Rule 62
 		 'loop', 6,
 sub
 { $_[0]->{ INWHILE }--;
                                       $factory->while(@_[2, 5])           }
 	],
-	[#Rule 62
+	[#Rule 63
 		 'loop', 3,
 sub
 { $factory->while(@_[3, 1])           }
 	],
-	[#Rule 63
+	[#Rule 64
 		 'loopvar', 4,
 sub
 { [ @_[1, 3, 4] ]                     }
 	],
-	[#Rule 64
+	[#Rule 65
 		 'loopvar', 4,
 sub
 { [ @_[1, 3, 4] ]                     }
 	],
-	[#Rule 65
+	[#Rule 66
 		 'loopvar', 2,
 sub
 { [ 0, @_[1, 2] ]                     }
 	],
-	[#Rule 66
+	[#Rule 67
 		 'wrapper', 5,
 sub
 { $factory->wrapper(@_[2, 4])         }
 	],
-	[#Rule 67
+	[#Rule 68
 		 'wrapper', 3,
 sub
 { $factory->wrapper(@_[3, 1])         }
 	],
-	[#Rule 68
+	[#Rule 69
 		 'try', 5,
 sub
 { $factory->try(@_[3, 4])             }
 	],
-	[#Rule 69
+	[#Rule 70
 		 'final', 5,
 sub
 { unshift(@{$_[5]}, [ @_[2,4] ]);
 				      $_[5];                              }
 	],
-	[#Rule 70
+	[#Rule 71
 		 'final', 5,
 sub
 { unshift(@{$_[5]}, [ undef, $_[4] ]);
 				      $_[5];                              }
 	],
-	[#Rule 71
+	[#Rule 72
 		 'final', 4,
 sub
 { unshift(@{$_[4]}, [ undef, $_[3] ]);
 				      $_[4];                              }
 	],
-	[#Rule 72
+	[#Rule 73
 		 'final', 3,
 sub
 { [ $_[3] ]                           }
 	],
-	[#Rule 73
+	[#Rule 74
 		 'final', 0,
 sub
 { [ 0 ] }
 	],
-	[#Rule 74
+	[#Rule 75
 		 'use', 2,
 sub
 { $factory->use($_[2])                }
 	],
-	[#Rule 75
+	[#Rule 76
+		 'raw', 2,
+sub
+{ $factory->raw($_[2])                }
+	],
+	[#Rule 77
 		 '@3-3', 0,
 sub
 { $_[0]->push_defblock();		  }
 	],
-	[#Rule 76
+	[#Rule 78
 		 'view', 6,
 sub
 { $factory->view(@_[2,5], 
 						     $_[0]->pop_defblock) }
 	],
-	[#Rule 77
+	[#Rule 79
 		 '@4-2', 0,
 sub
 { ${$_[0]->{ INJAVASCRIPT }}++;             }
 	],
-	[#Rule 78
+	[#Rule 80
 		 'javascript', 5,
 sub
 { ${$_[0]->{ INJAVASCRIPT }}--;
@@ -16203,17 +16459,17 @@ sub
 				      ? $factory->javascript($_[4])             
 				      : $factory->no_javascript();              }
 	],
-	[#Rule 79
+	[#Rule 81
 		 'filter', 5,
 sub
 { $factory->filter(@_[2,4])           }
 	],
-	[#Rule 80
+	[#Rule 82
 		 'filter', 3,
 sub
 { $factory->filter(@_[3,1])           }
 	],
-	[#Rule 81
+	[#Rule 83
 		 'defblock', 5,
 sub
 { my $name = join('/', @{ $_[0]->{ DEFBLOCKS } });
@@ -16222,28 +16478,28 @@ sub
 				      undef
 				    }
 	],
-	[#Rule 82
+	[#Rule 84
 		 'defblockname', 2,
 sub
 { push(@{ $_[0]->{ DEFBLOCKS } }, $_[2]);
 				      $_[2];
 				    }
 	],
-	[#Rule 83
+	[#Rule 85
 		 'blockname', 1, undef
 	],
-	[#Rule 84
+	[#Rule 86
 		 'blockname', 1,
 sub
 { $_[1] =~ s/^'(.*)'$/$1/; $_[1]      }
 	],
-	[#Rule 85
+	[#Rule 87
 		 'blockargs', 1, undef
 	],
-	[#Rule 86
+	[#Rule 88
 		 'blockargs', 0, undef
 	],
-	[#Rule 87
+	[#Rule 89
 		 'anonblock', 5,
 sub
 { local $" = ', ';
@@ -16251,417 +16507,417 @@ sub
 					  if $_[2];
 				      $factory->anon_block($_[4])         }
 	],
-	[#Rule 88
+	[#Rule 90
 		 'capture', 3,
 sub
 { $factory->capture(@_[1, 3])         }
 	],
-	[#Rule 89
+	[#Rule 91
 		 'macro', 6,
 sub
 { $factory->macro(@_[2, 6, 4])        }
 	],
-	[#Rule 90
+	[#Rule 92
 		 'macro', 3,
 sub
 { $factory->macro(@_[2, 3])           }
 	],
-	[#Rule 91
+	[#Rule 93
 		 'mdir', 1, undef
 	],
-	[#Rule 92
+	[#Rule 94
 		 'mdir', 4,
 sub
 { $_[3]                               }
 	],
-	[#Rule 93
+	[#Rule 95
 		 'margs', 2,
 sub
 { push(@{$_[1]}, $_[2]); $_[1]        }
 	],
-	[#Rule 94
+	[#Rule 96
 		 'margs', 2,
 sub
 { $_[1]                               }
 	],
-	[#Rule 95
+	[#Rule 97
 		 'margs', 1,
 sub
 { [ $_[1] ]                           }
 	],
-	[#Rule 96
+	[#Rule 98
 		 'metadata', 2,
 sub
 { push(@{$_[1]}, @{$_[2]}); $_[1]     }
 	],
-	[#Rule 97
+	[#Rule 99
 		 'metadata', 2, undef
 	],
-	[#Rule 98
+	[#Rule 100
 		 'metadata', 1, undef
 	],
-	[#Rule 99
+	[#Rule 101
 		 'meta', 3,
 sub
 { for ($_[3]) { s/^'//; s/'$//; 
 						       s/\\'/'/g  }; 
 					 [ @_[1,3] ] }
 	],
-	[#Rule 100
+	[#Rule 102
 		 'meta', 5,
 sub
 { [ @_[1,4] ] }
 	],
-	[#Rule 101
+	[#Rule 103
 		 'meta', 3,
 sub
 { [ @_[1,3] ] }
 	],
-	[#Rule 102
+	[#Rule 104
 		 'term', 1, undef
 	],
-	[#Rule 103
+	[#Rule 105
 		 'term', 1, undef
 	],
-	[#Rule 104
+	[#Rule 106
 		 'lterm', 3,
 sub
 { "[ $_[2] ]"                         }
 	],
-	[#Rule 105
+	[#Rule 107
 		 'lterm', 3,
 sub
 { "[ $_[2] ]"                         }
 	],
-	[#Rule 106
+	[#Rule 108
 		 'lterm', 2,
 sub
 { "[ ]"                               }
 	],
-	[#Rule 107
+	[#Rule 109
 		 'lterm', 3,
 sub
 { "{ $_[2]  }"                        }
 	],
-	[#Rule 108
+	[#Rule 110
 		 'sterm', 1,
 sub
 { $factory->ident($_[1])              }
 	],
-	[#Rule 109
+	[#Rule 111
 		 'sterm', 2,
 sub
 { $factory->identref($_[2])           }
 	],
-	[#Rule 110
+	[#Rule 112
 		 'sterm', 3,
 sub
 { $factory->quoted($_[2])             }
 	],
-	[#Rule 111
+	[#Rule 113
 		 'sterm', 1, undef
 	],
-	[#Rule 112
+	[#Rule 114
 		 'sterm', 1, undef
 	],
-	[#Rule 113
+	[#Rule 115
 		 'list', 2,
 sub
 { "$_[1], $_[2]"                      }
 	],
-	[#Rule 114
+	[#Rule 116
 		 'list', 2, undef
 	],
-	[#Rule 115
+	[#Rule 117
 		 'list', 1, undef
 	],
-	[#Rule 116
+	[#Rule 118
 		 'range', 3,
 sub
 { $_[1] . '..' . $_[3]                }
 	],
-	[#Rule 117
+	[#Rule 119
 		 'hash', 1, undef
 	],
-	[#Rule 118
+	[#Rule 120
 		 'hash', 0,
 sub
 { "" }
 	],
-	[#Rule 119
+	[#Rule 121
 		 'params', 2,
 sub
 { "$_[1], $_[2]"                      }
 	],
-	[#Rule 120
+	[#Rule 122
 		 'params', 2, undef
 	],
-	[#Rule 121
+	[#Rule 123
 		 'params', 1, undef
 	],
-	[#Rule 122
+	[#Rule 124
 		 'param', 3,
 sub
 { "$_[1]: $_[3]"                    }
 	],
-	[#Rule 123
+	[#Rule 125
 		 'param', 3,
 sub
 { "$_[1]: $_[3]"                    }
 	],
-	[#Rule 124
+	[#Rule 126
 		 'ident', 3,
 sub
 { push(@{$_[1]}, @{$_[3]}); $_[1]     }
 	],
-	[#Rule 125
+	[#Rule 127
 		 'ident', 3,
 sub
 { push(@{$_[1]}, 
 					   map {($_, 0)} split(/\./, $_[3]));
 				      $_[1];			          }
 	],
-	[#Rule 126
+	[#Rule 128
 		 'ident', 1, undef
 	],
-	[#Rule 127
+	[#Rule 129
 		 'node', 1,
 sub
 { [ $_[1], 0 ]                        }
 	],
-	[#Rule 128
+	[#Rule 130
 		 'node', 4,
 sub
 { [ $_[1], $factory->args($_[3]) ]    }
 	],
-	[#Rule 129
+	[#Rule 131
 		 'item', 1,
 sub
 { "'$_[1]'"                           }
 	],
-	[#Rule 130
+	[#Rule 132
 		 'item', 3,
 sub
 { $_[2]                               }
 	],
-	[#Rule 131
+	[#Rule 133
 		 'item', 2,
 sub
 { $_[0]->{ V1DOLLAR }
 				       ? "'$_[2]'" 
 				       : $factory->ident(["'$_[2]'", 0])  }
 	],
-	[#Rule 132
+	[#Rule 134
 		 'expr', 3,
 sub
 { "$_[1] $_[2] $_[3]"                 }
 	],
-	[#Rule 133
+	[#Rule 135
 		 'expr', 3,
 sub
 { "$_[1] $_[2] $_[3]"                 }
 	],
-	[#Rule 134
+	[#Rule 136
 		 'expr', 3,
 sub
 { "$_[1] $_[2] $_[3]"                 }
 	],
-	[#Rule 135
+	[#Rule 137
 		 'expr', 3,
 sub
-{ "int($_[1] / $_[3])"                }
+{ "Math.floor($_[1] / $_[3])"                }
 	],
-	[#Rule 136
+	[#Rule 138
 		 'expr', 3,
 sub
 { "$_[1] % $_[3]"                     }
 	],
-	[#Rule 137
+	[#Rule 139
 		 'expr', 3,
 sub
 { "$_[1] $CMPOP{ $_[2] } $_[3]"       }
 	],
-	[#Rule 138
+	[#Rule 140
 		 'expr', 3,
 sub
 { "$_[1]  + $_[3]"                    }
 	],
-	[#Rule 139
+	[#Rule 141
 		 'expr', 3,
 sub
 { "$_[1] && $_[3]"                    }
 	],
-	[#Rule 140
+	[#Rule 142
 		 'expr', 3,
 sub
 { "$_[1] || $_[3]"                    }
 	],
-	[#Rule 141
+	[#Rule 143
 		 'expr', 2,
 sub
 { "! $_[2]"                           }
 	],
-	[#Rule 142
+	[#Rule 144
 		 'expr', 5,
 sub
 { "$_[1] ? $_[3] : $_[5]"             }
 	],
-	[#Rule 143
+	[#Rule 145
 		 'expr', 3,
 sub
 { $factory->assign(@{$_[2]})          }
 	],
-	[#Rule 144
+	[#Rule 146
 		 'expr', 3,
 sub
 { "($_[2])"                           }
 	],
-	[#Rule 145
+	[#Rule 147
 		 'expr', 1, undef
 	],
-	[#Rule 146
+	[#Rule 148
 		 'setlist', 2,
 sub
 { push(@{$_[1]}, @{$_[2]}); $_[1]     }
 	],
-	[#Rule 147
+	[#Rule 149
 		 'setlist', 2, undef
 	],
-	[#Rule 148
+	[#Rule 150
 		 'setlist', 1, undef
 	],
-	[#Rule 149
+	[#Rule 151
 		 'assign', 3,
 sub
 { [ $_[1], $_[3] ]                    }
 	],
-	[#Rule 150
+	[#Rule 152
 		 'assign', 3,
 sub
 { [ @_[1,3] ]                         }
 	],
-	[#Rule 151
+	[#Rule 153
 		 'args', 2,
 sub
 { push(@{$_[1]}, $_[2]); $_[1]        }
 	],
-	[#Rule 152
+	[#Rule 154
 		 'args', 2,
 sub
 { push(@{$_[1]->[0]}, $_[2]); $_[1]   }
 	],
-	[#Rule 153
+	[#Rule 155
 		 'args', 4,
 sub
 { push(@{$_[1]->[0]}, "'', " . 
 				      $factory->assign(@_[2,4])); $_[1]  }
 	],
-	[#Rule 154
+	[#Rule 156
 		 'args', 2,
 sub
 { $_[1]                               }
 	],
-	[#Rule 155
+	[#Rule 157
 		 'args', 0,
 sub
 { [ [ ] ]                             }
 	],
-	[#Rule 156
+	[#Rule 158
 		 'lnameargs', 3,
 sub
 { push(@{$_[3]}, $_[1]); $_[3]        }
 	],
-	[#Rule 157
+	[#Rule 159
 		 'lnameargs', 1, undef
 	],
-	[#Rule 158
+	[#Rule 160
 		 'lvalue', 1, undef
 	],
-	[#Rule 159
+	[#Rule 161
 		 'lvalue', 3,
 sub
 { $factory->quoted($_[2])             }
 	],
-	[#Rule 160
+	[#Rule 162
 		 'lvalue', 1, undef
 	],
-	[#Rule 161
+	[#Rule 163
 		 'nameargs', 3,
 sub
 { [ [$factory->ident($_[2])], $_[3] ]   }
 	],
-	[#Rule 162
+	[#Rule 164
 		 'nameargs', 2,
 sub
 { [ @_[1,2] ] }
 	],
-	[#Rule 163
+	[#Rule 165
 		 'nameargs', 4,
 sub
 { [ @_[1,3] ] }
 	],
-	[#Rule 164
+	[#Rule 166
 		 'names', 3,
 sub
 { push(@{$_[1]}, $_[3]); $_[1] }
 	],
-	[#Rule 165
+	[#Rule 167
 		 'names', 1,
 sub
 { [ $_[1] ]                    }
 	],
-	[#Rule 166
+	[#Rule 168
 		 'name', 3,
 sub
 { $factory->quoted($_[2])  }
 	],
-	[#Rule 167
+	[#Rule 169
 		 'name', 1,
 sub
 { "'$_[1]'" }
 	],
-	[#Rule 168
+	[#Rule 170
 		 'name', 1, undef
 	],
-	[#Rule 169
+	[#Rule 171
 		 'filename', 3,
 sub
 { "$_[1].$_[3]" }
 	],
-	[#Rule 170
+	[#Rule 172
 		 'filename', 1, undef
 	],
-	[#Rule 171
+	[#Rule 173
 		 'filepart', 1, undef
 	],
-	[#Rule 172
+	[#Rule 174
 		 'filepart', 1, undef
 	],
-	[#Rule 173
+	[#Rule 175
 		 'filepart', 1, undef
 	],
-	[#Rule 174
+	[#Rule 176
 		 'quoted', 2,
 sub
 { push(@{$_[1]}, $_[2]) 
 				          if defined $_[2]; $_[1]         }
 	],
-	[#Rule 175
+	[#Rule 177
 		 'quoted', 0,
 sub
 { [ ]                                 }
 	],
-	[#Rule 176
+	[#Rule 178
 		 'quotable', 1,
 sub
 { $factory->ident($_[1])              }
 	],
-	[#Rule 177
+	[#Rule 179
 		 'quotable', 1,
 sub
 { $factory->text($_[1])               }
 	],
-	[#Rule 178
+	[#Rule 180
 		 'quotable', 1,
 sub
 { undef                               }
@@ -16750,7 +17006,7 @@ if (typeof Jemplate == 'undefined') {
 Jemplate.VERSION = '0.22';
 
 Jemplate.process = function() {
-    var jemplate = new Jemplate();
+    var jemplate = new Jemplate(Jemplate.prototype.config);
     return jemplate.process.apply(jemplate, arguments);
 }
 
@@ -16761,38 +17017,83 @@ if (! Jemplate.templateMap)
 
 var proto = Jemplate.prototype = {};
 
+proto.config = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    FILTERS: {},
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+proto.defaults = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+
+Jemplate.init = function(config) {
+ 
+    Jemplate.prototype.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof Jemplate.prototype.config[i] == "undefined") {
+            Jemplate.prototype.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
+}
+
 proto.init = function(config) {
-    this.config = config ||
-    {
-        AUTO_RESET: true,
-        BLOCKS: {},
-        CONTEXT: null,
-        DEBUG_UNDEF: false,
-        DEFAULT: null,
-        ERROR: null,
-        EVAL_JAVASCRIPT: false,
-        FILTERS: {},
-        INCLUDE_PATH: [''],
-        INTERPOLATE: false,
-        OUTPUT: null,
-        PLUGINS: {},
-        POST_PROCESS: [],
-        PRE_PROCESS: [],
-        PROCESS: null,
-        RECURSION: false,
-        STASH: null,
-        TOLERANT: null,
-        VARIABLES: {},
-        WRAPPER: []
-    };
+    
+    this.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof this.config[i] == "undefined") {
+            this.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
 }
 
 proto.process = function(template, data, output) {
     var context = this.config.CONTEXT || new Jemplate.Context();
     context.config = this.config;
 
-    context.stash = this.config.STASH || new Jemplate.Stash();
-    context.stash.__config__ = this.config;
+    context.stash = new Jemplate.Stash(this.config.STASH, this.config);
 
     context.__filter__ = new Jemplate.Filter();
     context.__filter__.config = this.config;
@@ -16804,7 +17105,17 @@ proto.process = function(template, data, output) {
 
     var proc = function(input) {
         try {
+            if (typeof context.config.PRE_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.PRE_PROCESS];                
+            for (var i = 0; i < context.config.PRE_PROCESS.length; i++) {
+                context.process(context.config.PRE_PROCESS[i]);
+            }
+            
             result = context.process(template, input);
+            
+            if (typeof context.config.POST_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.POST_PROCESS];
+            for (i = 0; i < context.config.POST_PROCESS.length; i++) {
+                context.process(context.config.POST_PROCESS[i]);
+            }
         }
         catch(e) {
             if (! String(e).match(/Jemplate\.STOP\n/))
@@ -16886,7 +17197,8 @@ proto.plugin = function(name, args) {
         throw "Unknown plugin name ':" + name + "'";
 
     // The Context object (this) is passed as the first argument to the plugin.
-    return new window[name](this, args);
+	var func = eval(name);
+    return new func(this, args);
 }
 
 proto.filter = function(text, name, args) {
@@ -17040,8 +17352,16 @@ proto.filters.replace = function(text, args) {
 // Jemplate.Stash class
 //------------------------------------------------------------------------------
 if (typeof Jemplate.Stash == 'undefined') {
-    Jemplate.Stash = function() {
-        this.data = {};
+    Jemplate.Stash = function(stash, config) {
+        this.__config__ = config;
+		
+		this.data = {
+			GLOBAL : this.__config__.SCOPE			
+		};
+		this.LOCAL_ANCHOR = {};
+		this.data.LOCAL = this.LOCAL_ANCHOR;
+		
+		this.update(stash);
     };
 }
 
@@ -17049,7 +17369,10 @@ proto = Jemplate.Stash.prototype;
 
 proto.clone = function(args) {
     var data = this.data;
-    this.data = {};
+    this.data = {
+		GLOBAL : this.__config__.SCOPE
+	};
+	this.data.LOCAL = this.LOCAL_ANCHOR;
     this.update(data);
     this.update(args);
     this.data._PARENT = data;
@@ -17062,87 +17385,221 @@ proto.declone = function(args) {
 proto.update = function(args) {
     if (typeof args == 'undefined') return;
     for (var key in args) {
-        var value = args[key];
-        this.set(key, value);
+        if (key != 'GLOBAL' && key != 'LOCAL') {
+	        this.set(key, args[key]);
+		}
     }
 }
 
-proto.get = function(key) {
+proto.get = function(ident, args) {
     var root = this.data;
-    if (key instanceof Array) {
-        for (var i = 0; i < key.length; i += 2) {
-            var args = key.slice(i, i+2);
-            args.unshift(root);
-            value = this._dotop.apply(this, args);
+    
+    var value;
+    
+    if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+        
+        if (typeof ident == 'string') {
+            ident = ident.split('.');
+            var newIdent = [];
+            for (var i = 0; i < ident.length; i++) {
+                newIdent.push(ident.replace(/\(.*$/,''));
+                newIdent.push(0);
+            }
+            ident = newIdent;
+        }
+        
+        for (var i = 0; i < ident.length; i += 2) {
+            var dotopArgs = ident.slice(i, i+2);
+            dotopArgs.unshift(root);
+            value = this._dotop.apply(this, dotopArgs);
             if (typeof value == 'undefined')
                 break;
             root = value;
         }
     }
     else {
-        value = this._dotop(root, key);
+        value = this._dotop(root, ident, args);
     }
 
-    if (typeof value == 'undefined') {
+    if (typeof value == 'undefined' || value == null) {
         if (this.__config__.DEBUG_UNDEF)
-            throw("undefined value found while using DEGUG_UNDEF");
+            throw("undefined value found while using DEBUG_UNDEF");
         value = '';
     }
 
     return value;
 }
 
-proto.set = function(key, value, set_default) {
-    if (key instanceof Array) {
-        var data = this.get(key[0]) || {};
-        key = key[2];
-    }
-    else {
-        data = this.data;
+
+
+proto.set = function(ident, value, set_default) {
+    
+    var root, result, error;
+    
+    root = this.data;
+    
+    while (true) {
+        if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+            
+            if (typeof ident == 'string') {
+                ident = ident.split('.');
+                var newIdent = [];
+                for (var i = 0; i < ident.length; i++) {
+                    newIdent.push(ident.replace(/\(.*$/,''));
+                    newIdent.push(0);
+                }
+                ident = newIdent;
+            }
+            
+            for (var i = 0; i < ident.length - 2; i += 2) {
+                var dotopArgs = ident.slice(i, i+2);
+                dotopArgs.unshift(root);
+                dotopArgs.push(1);
+                result = this._dotop.apply(this, dotopArgs);
+                if (typeof value == 'undefined')
+                    break;
+                root = result;
+            }
+            
+            var assignArgs = ident.slice(ident.length-2, ident.length);
+            assignArgs.unshift(root);
+            assignArgs.push(value);
+            assignArgs.push(set_default);
+            
+            
+            result = this._assign.apply(this, assignArgs);
+        } else {
+            result = this._assign(root, ident, 0, value, set_default);
+        }
+        break;
     }
-    if (! (set_default && (typeof data[key] != 'undefined')))
-        data[key] = value;
+    
+    return (typeof result != 'undefined') ? result : '';
 }
 
-proto._dotop = function(root, item, args) {
-    if (typeof item == 'undefined' ||
-        typeof item == 'string' && item.match(/^[\._]/)) {
+
+
+proto._dotop = function(root, item, args, lvalue) {    
+    if (root == this.LOCAL_ANCHOR) root = this.data;
+	var atroot = root == this.data;
+    
+    var value,result = undefined;
+    
+   	var is_function_call = args instanceof Array;
+   	
+   	args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
         return undefined;
     }
 
-    if ((! args) &&
-        (typeof root == 'object') &&
-        (!(root instanceof Array) || (typeof item == 'number')) &&
-        (typeof root[item] != 'undefined')) {
-        var value = root[item];
-        if (typeof value == 'function')
-            value = value.apply(root);
-        return value;
+
+    //root is complex object, not scalar
+    if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) {
+        
+		if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null
+            if (typeof root[item] == 'function') {
+                result = root[item].apply(root,args);
+            } else {
+                return root[item];
+            }
+        } else if (lvalue) {
+            return root[item] = {};
+        } else if (this.hash_functions[item] && !atroot || item == 'import') {
+            args.unshift(root);
+            result = this.hash_functions[item].apply(this,args);
+        } else if (item instanceof Array) {
+            result = {};
+            
+            for (var i = 0; i < item.length; i++) result[item[i]] = root[item[i]];
+            return result;
+        }
+    } else if (root instanceof Array) {
+        if (this.list_functions[item]) {
+            args.unshift(root);
+            result = this.list_functions[item].apply(this,args);
+        } else if (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' ) {
+            if (typeof root[item] != 'function') return root[item];
+            result = root[item].apply(this, args);
+        } else if (item instanceof Array) {
+            for (var i = 0; i < item.length; i++) result.push(root[item[i]]);
+            return result;
+        }
+    } else if (this.string_functions[item] && !lvalue) {
+        args.unshift(root);
+        result = this.string_functions[item].apply(this, args);
+    } else if (this.list_functions[item] && !lvalue) {
+        args.unshift([root]);
+        result = this.list_functions[item].apply(this,args);
+    } else {
+        result = undefined;
     }
+    
+    
+    if (result instanceof Array) {
+		if (typeof result[0] == 'undefined' && typeof result[1] != 'undefined') {
+	        throw result[1];
+	    }
+	}
+    
+    return result;
+
+}
 
-    if (typeof root == 'string' && this.string_functions[item])
-        return this.string_functions[item](root, args);
-    if (root instanceof Array && this.list_functions[item])
-        return this.list_functions[item](root, args);
-    if (typeof root == 'object' && this.hash_functions[item])
-        return this.hash_functions[item](root, args);
-    if (typeof root[item] == 'function')
-        return root[item].apply(root, args);
 
+proto._assign = function(root, item, args, value, set_default) {
+    var atroot = root == this.data;
+    var result;
+    
+    args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
+        return undefined;
+    }
+    
+    if (atroot || root.constructor == Object || root == this.data.GLOBAL) {
+		
+		if (root == this.LOCAL_ANCHOR) root = this.data;
+			 
+		if (!(set_default && typeof root[item] != 'undefined')) {
+            if (atroot && item == 'GLOBAL') throw "Attempt to modify GLOBAL access modifier"
+			if (atroot && item == 'LOCAL') throw "Attempt to modify LOCAL access modifier"
+			
+			return root[item] = value;
+        } 
+    } else if ((root instanceof Array) && (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' )) {
+        if (!(set_default && typeof root[item] != 'undefined')) {
+            return root[item] = value;
+        }
+    } else if ( (root.constructor != Object) && (root instanceof Object) ) {
+        try {
+            result = root[item].apply(root,args);
+        } catch (e) {
+        }
+    } else {
+        throw 'dont know how to assign to [' + root + '.' + item +']';
+    }
+    
     return undefined;
 }
 
+
 proto.string_functions = {};
 
+// typeof
+proto.string_functions['typeof'] = function(value) {
+    return typeof value;
+}
+
 // chunk(size)     negative size chunks from end
-proto.string_functions.chunk = function(string, args) {
-    var size = args[0];
+proto.string_functions.chunk = function(string, size) {
+    //var size = args;
     var list = new Array();
     if (! size)
         size = 1;
     if (size < 0) {
         size = 0 - size;
-        for (i = string.length - size; i >= 0; i = i - size)
+        for (var i = string.length - size; i >= 0; i = i - size)
             list.unshift(string.substr(i, size));
         if (string.length % size)
             list.unshift(string.substr(0, string.length % size));
@@ -17174,15 +17631,15 @@ proto.string_functions.list = function(string) {
 }
 
 // match(re)       get list of matches
-proto.string_functions.match = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
+proto.string_functions.match = function(string, re, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);
     var list = string.match(regexp);
     return list;
 }
 
 // repeat(n)       repeated n times
 proto.string_functions.repeat = function(string, args) {
-    var n = args[0] || 1;
+    var n = args || 1;
     var output = '';
     for (var i = 0; i < n; i++) {
         output += string;
@@ -17190,19 +17647,17 @@ proto.string_functions.repeat = function(string, args) {
     return output;
 }
 
-// replace(re, sub)    replace instances of re with sub
-proto.string_functions.replace = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var sub = args[1];
-    if (! sub)
-        sub  = '';
-    var output = string.replace(regexp, sub);
-    return output;
+// replace(re, sub, global)    replace instances of re with sub
+proto.string_functions.replace = function(string, re, sub, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);    
+    if (! sub) sub  = '';
+
+    return string.replace(regexp, sub);
 }
 
 // search(re)      true if value matches re
-proto.string_functions.search = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.search = function(string, re) {
+    var regexp = new RegExp(re);
     return (string.search(regexp) >= 0) ? 1 : 0;
 }
 
@@ -17212,8 +17667,8 @@ proto.string_functions.size = function(string) {
 }
 
 // split(re)       split string on re
-proto.string_functions.split = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.split = function(string, re) {
+    var regexp = new RegExp(re);
     var list = string.split(regexp);
     return list;
 }
@@ -17222,8 +17677,18 @@ proto.string_functions.split = function(string, args) {
 
 proto.list_functions = {};
 
-proto.list_functions.join = function(list, args) {
-    return list.join(args[0]);
+// typeof
+proto.list_functions['typeof'] = function(list) {
+    return 'array';
+};
+
+
+proto.list_functions.list = function(list) {
+    return list;
+};
+
+proto.list_functions.join = function(list, str) {
+    return list.join(str);
 };
 
 proto.list_functions.sort = function(list,key) {
@@ -17251,8 +17716,8 @@ proto.list_functions.nsort = function(list) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
-proto.list_functions.grep = function(list, args) {
-    var regexp = new RegExp(args[0]);
+proto.list_functions.grep = function(list, re) {
+    var regexp = new RegExp(re);
     var result = [];
     for (var i = 0; i < list.length; i++) {
         if (list[i].match(regexp))
@@ -17281,7 +17746,7 @@ proto.list_functions.reverse = function(list) {
     return result;
 }
 
-proto.list_functions.merge = function(list, args) {
+proto.list_functions.merge = function(list /*, ... args */) {
     var result = [];
     var push_all = function(elem) {
         if (elem instanceof Array) {
@@ -17294,28 +17759,30 @@ proto.list_functions.merge = function(list, args) {
         }
     }
     push_all(list);
-    for (var i = 0; i < args.length; i++) {
-        push_all(args[i]);
+    for (var i = 1; i < arguments.length; i++) {
+        push_all(arguments[i]);
     }
     return result;
 }
 
-proto.list_functions.slice = function(list, args) {
-    return list.slice(args[0], args[1]);
+proto.list_functions.slice = function(list, start, end) {
+    // To make it like slice in TT
+    // See rt53453
+    if ( end == -1 ) {
+        return list.slice( start );
+    }
+    return list.slice( start, end + 1 );
 }
 
-proto.list_functions.splice = function(list, args) {
-    if (args.length == 1)
-        return list.splice(args[0]);
-    if (args.length == 2)
-        return list.splice(args[0], args[1]);
-    if (args.length == 3)
-        return list.splice(args[0], args[1], args[2]);
-    return null;
+proto.list_functions.splice = function(list /*, ... args */ ) {
+    var args = Array.prototype.slice.call(arguments);
+    args.shift();
+    
+    return list.splice.apply(list,args);
 }
 
-proto.list_functions.push = function(list, args) {
-    list.push(args[0]);
+proto.list_functions.push = function(list, value) {
+    list.push(value);
     return list;
 }
 
@@ -17323,8 +17790,8 @@ proto.list_functions.pop = function(list) {
     return list.pop();
 }
 
-proto.list_functions.unshift = function(list, args) {
-    list.unshift(args[0]);
+proto.list_functions.unshift = function(list, value) {
+    list.unshift(value);
     return list;
 }
 
@@ -17350,6 +17817,11 @@ proto.list_functions.last = function(list) {
 
 proto.hash_functions = {};
 
+// typeof
+proto.hash_functions['typeof'] = function(hash) {
+    return 'object';
+};
+
 
 // each            list of alternating keys/values
 proto.hash_functions.each = function(hash) {
@@ -17360,20 +17832,17 @@ proto.hash_functions.each = function(hash) {
 }
 
 // exists(key)     does key exist?
-proto.hash_functions.exists = function(hash, args) {
-    return ( typeof( hash[args[0]] ) == "undefined" ) ? 0 : 1;
+proto.hash_functions.exists = function(hash, key) {
+    return ( typeof( hash[key] ) == "undefined" ) ? 0 : 1;
 }
 
-// FIXME proto.hash_functions.import blows everything up
-//
 // import(hash2)   import contents of hash2
 // import          import into current namespace hash
-//proto.hash_functions.import = function(hash, args) {
-//    var hash2 = args[0];
-//    for ( var key in hash2 )
-//        hash[key] = hash2[key];
-//    return '';
-//}
+proto.hash_functions['import'] = function(hash, hash2) {    
+    for ( var key in hash2 )
+        hash[key] = hash2[key];
+    return '';
+}
 
 // keys            list of keys
 proto.hash_functions.keys = function(hash) {
@@ -17384,10 +17853,10 @@ proto.hash_functions.keys = function(hash) {
 }
 
 // list            returns alternating key, value
-proto.hash_functions.list = function(hash, args) {
-    var what = '';
-    if ( args )
-        what = args[0];
+proto.hash_functions.list = function(hash, what) {
+    //var what = '';
+    //if ( args )
+        //what = args[0];
 
     var list = new Array();
     var key;
@@ -17415,6 +17884,11 @@ proto.hash_functions.nsort = function(hash) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
+// item           return a value by key
+proto.hash_functions.item = function(hash, key) {
+    return hash[key];
+}
+
 // size            number of pairs
 proto.hash_functions.size = function(hash) {
     var size = 0;
@@ -17440,9 +17914,23 @@ proto.hash_functions.values = function(hash) {
     return list;
 }
 
+proto.hash_functions.pairs = function(hash) {
+    var list = new Array();
+    var keys = new Array();
+    for ( var key in hash ) {
+        keys.push( key );
+    }
+    keys.sort();
+    for ( var key in keys ) {
+        key = keys[key]
+        list.push( { 'key': key, 'value': hash[key] } );
+    }
+    return list;
+}
+
 //  delete
-proto.hash_functions.remove = function(hash, args) {
-    return delete hash[args[0]];
+proto.hash_functions.remove = function(hash, key) {
+    return delete hash[key];
 }
 proto.hash_functions['delete'] = proto.hash_functions.remove;
 
@@ -17465,6 +17953,9 @@ if (typeof Jemplate.Iterator == 'undefined') {
             this.object_keys = object_keys.sort();
             this.size = object_keys.length;
             this.max  = this.size -1;
+        } else if (typeof object == 'undefined' || object == null || object == '') {
+            this.object = null;
+            this.max  = -1;
         }
     }
 }
@@ -17501,7 +17992,7 @@ proto.get_next = function(should_init) {
             return [this.object_keys[index], false];
         }
     } else {
-        if (index < object.length) {
+        if (index <= this.max) {
             this.prev = index > 0 ? object[index - 1] : "";
             this.next = index < this.max ? object[index +1] : "";
             return [object[index], false];
@@ -17686,8 +18177,8 @@ sub json_json2_internal {
 var JSON;
 
 /*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -17695,44 +18186,48 @@ var JSON;
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
 
-            This is the toJSON method added to Dates:
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -17740,35 +18235,24 @@ var JSON;
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -17779,16 +18263,22 @@ var JSON;
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -17808,45 +18298,57 @@ var JSON;
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -17856,175 +18358,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -18032,123 +18538,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
+
 
+// If the JSON object does not yet have a parse method, give it one.
 
-            parse: function (text, reviver) {
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
+
 
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
 
-// We split the first stage into 4 regexp operations in order to work around
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 
 Jemplate.JSON = {
@@ -18192,8 +18699,8 @@ Jemplate.JSON = {
 sub json2 {
     <<'...';
 /*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -18201,44 +18708,48 @@ sub json2 {
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
 
-            This is the toJSON method added to Dates:
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -18246,35 +18757,24 @@ sub json2 {
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -18285,16 +18785,22 @@ sub json2 {
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -18314,45 +18820,57 @@ sub json2 {
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -18362,175 +18880,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -18538,123 +19060,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
+
 
+// If the JSON object does not yet have a parse method, give it one.
 
-            parse: function (text, reviver) {
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
+
 
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
 
-// We split the first stage into 4 regexp operations in order to work around
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 ...
 }
@@ -19263,7 +19786,7 @@ use warnings;
 sub main { return &kernel }
 sub kernel {
     <<'...';
-if(typeof Jemplate=="undefined"){var Jemplate=function(){this.init.apply(this,arguments)}}Jemplate.VERSION="0.22";Jemplate.process=function(){var A=new Jemplate();return A.process.apply(A,arguments)};(function(){if(!Jemplate.templateMap){Jemplate.templateMap={}}var B=Jemplate.prototype={};B.init=function(C){this.config=C||{AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,FILTERS:{},INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]}};B.process=function(H,I,E){var G=this.config.CONTEXT||new Jemplate.Context();G.config=this.config;G.stash=this.config.STASH||new Jemplate.Stash();G.stash.__config__=this.config;G.__filter__=new Jemplate.Filter();G.__filter__.config=this.config;G.__plugin__=new Jemplate.Plugin();G.__plugin__.config=this.config;var C;var D=function(J){try{C=G.process(H,J)}catch(L){if(!String(L).match(/Jemplate\.STOP\n/)){throw (L)}C=L.toString().replace(/Jemplate\.STOP\n/,"")}if(typeof E=="undefined"){return C}if(typeof E=="function"){E(C);return null}if(typeof (E)=="string"||E instanceof String){if(E.match(/^#[\w\-]+$/)){var M=E.replace(/^#/,"");var K=document.getElementById(M);if(typeof K=="undefined"){throw ('No element found with id="'+M+'"')}K.innerHTML=C;return null}}else{E.innerHTML=C;return null}throw ("Invalid arguments in call to Jemplate.process");return 1};if(typeof I=="function"){I=I()}else{if(typeof I=="string"){var F=I;Jemplate.Ajax.processGet(F,function(J){D(J)});return null}}return D(I)};if(typeof Jemplate.Context=="undefined"){Jemplate.Context=function(){}}B=Jemplate.Context.prototype;B.include=function(D,C){return this.process(D,C,true)};B.process=function(E,D,G){if(G){this.stash.clone(D)}else{this.stash.update(D)}var F=Jemplate.templateMap[E];if(typeof F=="undefined"){throw ('No Jemplate template named "'+E+'" available')}var C=F(this);if(G){this.stash.declone()}return C};B.set_error=function(D,C){this._error=[D,C];return D};B.plugin=function(D,C){if(typeof D=="undefined"){throw"Unknown plugin name ':"+D+"'"}return new window[D](this,C)};B.filter=function(E,D,C){if(D=="null"){D="null_filter"}if(typeof this.__filter__.filters[D]=="function"){return this.__filter__.filters[D](E,C,this)}else{throw"Unknown filter name ':"+D+"'"}};if(typeof Jemplate.Plugin=="undefined"){Jemplate.Plugin=function(){}}B=Jemplate.Plugin.prototype;B.plugins={};if(typeof Jemplate.Filter=="undefined"){Jemplate.Filter=function(){}}B=Jemplate.Filter.prototype;B.filters={};B.filters.null_filter=function(C){return""};B.filters.upper=function(C){return C.toUpperCase()};B.filters.lower=function(C){return C.toLowerCase()};B.filters.ucfirst=function(E){var D=E.charAt(0);var C=E.substr(1);return D.toUpperCase()+C};B.filters.lcfirst=function(E){var D=E.charAt(0);var C=E.substr(1);return D.toLowerCase()+C};B.filters.trim=function(C){return C.replace(/^\s+/g,"").replace(/\s+$/g,"")};B.filters.collapse=function(C){return C.replace(/^\s+/g,"").replace(/\s+$/g,"").replace(/\s+/," ")};B.filters.html=function(C){C=C.replace(/&/g,"&amp;");C=C.replace(/</g,"&lt;");C=C.replace(/>/g,"&gt;");C=C.replace(/"/g,"&quot;");return C};B.filters.html_para=function(D){var C=D.split(/(?:\r?\n){2,}/);return"<p>\n"+C.join("\n</p>\n\n<p>\n")+"</p>\n"};B.filters.html_break=function(C){return C.replace(/(\r?\n){2,}/g,"$1<br />$1<br />$1")};B.filters.html_line_break=function(C){return C.replace(/(\r?\n)/g,"$1<br />$1")};B.filters.uri=function(C){return encodeURIComponent(C)};B.filters.url=function(C){return encodeURI(C)};B.filters.indent=function(H,D){var F=D[0];if(!H){return null}if(typeof F=="undefined"){F=4}var G="";if(typeof F=="number"||String(F).match(/^\d$/)){for(var E=0;E<F;E++){G+=" "}}else{G=F}var C=H.replace(/^/gm,G);return C};B.filters.truncate=function(F,D){var C=D[0];if(!F){return null}if(!C){C=32}if(F.length<C){return F}var E=C-3;return F.substr(0,E)+"..."};B.filters.repeat=function(F,C){if(!F){return null}if(!C||C==0){C=1}if(C==1){return F}var D=F;for(var E=1;E<C;E++){D+=F}return D};B.filters.replace=function(G,D){if(!G){return null}var C=D[0];var F=D[1];if(!C){C=""}if(!F){F=""}var E=new RegExp(C,"g");return G.replace(E,F)};if(typeof Jemplate.Stash=="undefined"){Jemplate.Stash=function(){this.data={}}}B=Jemplate.Stash.prototype;B.clone=function(C){var D=this.data;this.data={};this.update(D);this.update(C);this.data._PARENT=D};B.declone=function(C){this.data=this.data._PARENT||this.data};B.update=function(C){if(typeof C=="undefined"){return }for(var D in C){var E=C[D];this.set(D,E)}};B.get=function(F){var C=this.data;if(F instanceof Array){for(var E=0;E<F.length;E+=2){var D=F.slice(E,E+2);D.unshift(C);value=this._dotop.apply(this,D);if(typeof value=="undefined"){break}C=value}}else{value=this._dotop(C,F)}if(typeof value=="undefined"){if(this.__config__.DEBUG_UNDEF){throw ("undefined value found while using DEGUG_UNDEF")}value=""}return value};B.set=function(D,F,C){if(D instanceof Array){var E=this.get(D[0])||{};D=D[2]}else{E=this.data}if(!(C&&(typeof E[D]!="undefined"))){E[D]=F}};B._dotop=function(C,E,D){if(typeof E=="undefined"||typeof E=="string"&&E.match(/^[\._]/)){return undefined}if((!D)&&(typeof C=="object")&&(!(C instanceof Array)||(typeof E=="number"))&&(typeof C[E]!="undefined")){var F=C[E];if(typeof F=="function"){F=F.apply(C)}return F}if(typeof C=="string"&&this.string_functions[E]){return this.string_functions[E](C,D)}if(C instanceof Array&&this.list_functions[E]){return this.list_functions[E](C,D)}if(typeof C=="object"&&this.hash_functions[E]){return this.hash_functions[E](C,D)}if(typeof C[E]=="function"){return C[E].apply(C,D)}return undefined};B.string_functions={};B.string_functions.chunk=function(D,C){var E=C[0];var F=new Array();if(!E){E=1}if(E<0){E=0-E;for(i=D.length-E;i>=0;i=i-E){F.unshift(D.substr(i,E))}if(D.length%E){F.unshift(D.substr(0,D.length%E))}}else{for(i=0;i<D.length;i=i+E){F.push(D.substr(i,E))}}return F};B.string_functions.defined=function(C){return 1};B.string_functions.hash=function(C){return{"value":C}};B.string_functions.length=function(C){return C.length};B.string_functions.list=function(C){return[C]};B.string_functions.match=function(D,C){var F=new RegExp(C[0],"gm");var E=D.match(F);return E};B.string_functions.repeat=function(E,D){var G=D[0]||1;var C="";for(var F=0;F<G;F++){C+=E}return C};B.string_functions.replace=function(E,D){var G=new RegExp(D[0],"gm");var F=D[1];if(!F){F=""}var C=E.replace(G,F);return C};B.string_functions.search=function(D,C){var E=new RegExp(C[0]);return(D.search(E)>=0)?1:0};B.string_functions.size=function(C){return 1};B.string_functions.split=function(D,C){var F=new RegExp(C[0]);var E=D.split(F);return E};B.list_functions={};B.list_functions.join=function(D,C){return D.join(C[0])};B.list_functions.sort=function(D,C){if(typeof (C)!="undefined"&&C!=""){return D.sort(function(F,E){if(F[C]==E[C]){return 0}else{if(F[C]>E[C]){return 1}else{return -1}}})}return D.sort()};B.list_functions.nsort=function(C){return C.sort(function(E,D){return(E-D)})};B.list_functions.grep=function(G,D){var F=new RegExp(D[0]);var C=[];for(var E=0;E<G.length;E++){if(G[E].match(F)){C.push(G[E])}}return C};B.list_functions.unique=function(G){var C=[];var D={};for(var E=0;E<G.length;E++){var F=G[E];if(!D[F]){C.push(F)}D[F]=true}return C};B.list_functions.reverse=function(E){var C=[];for(var D=E.length-1;D>=0;D--){C.push(E[D])}return C};B.list_functions.merge=function(G,E){var C=[];var D=function(I){if(I instanceof Array){for(var H=0;H<I.length;H++){C.push(I[H])}}else{C.push(I)}};D(G);for(var F=0;F<E.length;F++){D(E[F])}return C};B.list_functions.slice=function(D,C){return D.slice(C[0],C[1])};B.list_functions.splice=function(D,C){if(C.length==1){return D.splice(C[0])}if(C.length==2){return D.splice(C[0],C[1])}if(C.length==3){return D.splice(C[0],C[1],C[2])}return null};B.list_functions.push=function(D,C){D.push(C[0]);return D};B.list_functions.pop=function(C){return C.pop()};B.list_functions.unshift=function(D,C){D.unshift(C[0]);return D};B.list_functions.shift=function(C){return C.shift()};B.list_functions.first=function(C){return C[0]};B.list_functions.size=function(C){return C.length};B.list_functions.max=function(C){return C.length-1};B.list_functions.last=function(C){return C.slice(-1)};B.hash_functions={};B.hash_functions.each=function(E){var D=new Array();for(var C in E){D.push(C,E[C])}return D};B.hash_functions.exists=function(D,C){return(typeof (D[C[0]])=="undefined")?0:1};B.hash_functions.keys=function(E){var D=new Array();for(var C in E){D.push(C)}return D};B.hash_functions.list=function(G,C){var F="";if(C){F=C[0]}var E=new Array();var D;if(F=="keys"){for(D in G){E.push(D)}}else{if(F=="values"){for(D in G){E.push(G[D])}}else{if(F=="each"){for(D in G){E.push(D,G[D])}}else{for(D in G){E.push({"key":D,"value":G[D]})}}}}return E};B.hash_functions.nsort=function(E){var D=new Array();for(var C in E){D.push(C)}return D.sort(function(G,F){return(G-F)})};B.hash_functions.size=function(E){var D=0;for(var C in E){D++}return D};B.hash_functions.sort=function(E){var D=new Array();for(var C in E){D.push(C)}return D.sort()};B.hash_functions.values=function(E){var D=new Array();for(var C in E){D.push(E[C])}return D};B.hash_functions.remove=function(D,C){return delete D[C[0]]};B.hash_functions["delete"]=B.hash_functions.remove;if(typeof Jemplate.Iterator=="undefined"){Jemplate.Iterator=function(D){if(D instanceof Array){this.object=D;this.size=D.length;this.max=this.size-1}else{if(D instanceof Object){this.object=D;var C=new Array;for(var E in D){C[C.length]=E}this.object_keys=C.sort();this.size=C.length;this.max=this.size-1}}}}B=Jemplate.Iterator.prototype;B.get_first=function(){this.index=0;this.first=1;this.last=0;this.count=1;return this.get_next(1)};B.get_next=function(C){var E=this.object;var D;if(typeof (C)!="undefined"&&C){D=this.index}else{D=++this.index;this.first=0;this.count=this.index+1;if(this.index==this.size-1){this.last=1}}if(typeof E=="undefined"){throw ("No object to iterate")}if(this.object_keys){if(D<this.object_keys.length){this.prev=D>0?this.object_keys[D-1]:"";this.next=D<this.max?this.object_keys[D+1]:"";return[this.object_keys[D],false]}}else{if(D<E.length){this.prev=D>0?E[D-1]:"";this.next=D<this.max?E[D+1]:"";return[E[D],false]}}return[null,true]};var A="stub that doesn't do anything. Try including the jQuery, YUI, or XHR option when building the runtime";Jemplate.Ajax={get:function(C,D){throw ("This is a Jemplate.Ajax.get "+A)},processGet:function(C,D){throw ("This is a Jemplate.Ajax.processGet "+A)},post:function(C,D){throw ("This is a Jemplate.Ajax.post "+A)}};Jemplate.JSON={parse:function(C){throw ("This is a Jemplate.JSON.parse "+A)},stringify:function(C){throw ("This is a Jemplate.JSON.stringify "+A)}}}())
+if(typeof Jemplate=="undefined"){var Jemplate=function(){this.init.apply(this,arguments)}}Jemplate.VERSION="0.22";Jemplate.process=function(){var A=new Jemplate(Jemplate.prototype.config);return A.process.apply(A,arguments)};(function(){if(!Jemplate.templateMap){Jemplate.templateMap={}}var proto=Jemplate.prototype={};proto.config={AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,GLOBAL:true,SCOPE:this,FILTERS:{},INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]};proto.defaults={AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,GLOBAL:true,SCOPE:this,INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]};Jemplate.init=function(config){Jemplate.prototype.config=config||{};for(var i in Jemplate.prototype.defaults){if(typeof Jemplate.prototype.config[i]=="undefined"){Jemplate.prototype.config[i]=Jemplate.prototype.defaults[i]}}};proto.init=function(config){this.config=config||{};for(var i in Jemplate.prototype.defaults){if(typeof this.config[i]=="undefined"){this.config[i]=Jemplate.prototype.defaults[i]}}};proto.process=function(template,data,output){var context=this.config.CONTEXT||new Jemplate.Context();context.config=this.config;context.stash=new Jemplate.Stash(this.config.STASH,this.config);context.__filter__=new Jemplate.Filter();context.__filter__.config=this.config;context.__plugin__=new Jemplate.Plugin();context.__plugin__.config=this.config;var result;var proc=function(input){try{if(typeof context.config.PRE_PROCESS=="string"){context.config.PRE_PROCESS=[context.config.PRE_PROCESS]}for(var i=0;i<context.config.PRE_PROCESS.length;i++){context.process(context.config.PRE_PROCESS[i])}result=context.process(template,input);if(typeof context.config.POST_PROCESS=="string"){context.config.PRE_PROCESS=[context.config.POST_PROCESS]}for(i=0;i<context.config.POST_PROCESS.length;i++){context.process(context.config.POST_PROCESS[i])}}catch(e){if(!String(e).match(/Jemplate\.STOP\n/)){throw (e)}result=e.toString().replace(/Jemplate\.STOP\n/,"")}if(typeof output=="undefined"){return result}if(typeof output=="function"){output(result);return null}if(typeof (output)=="string"||output instanceof String){if(output.match(/^#[\w\-]+$/)){var id=output.replace(/^#/,"");var element=document.getElementById(id);if(typeof element=="undefined"){throw ('No element found with id="'+id+'"')}element.innerHTML=result;return null}}else{output.innerHTML=result;return null}throw ("Invalid arguments in call to Jemplate.process");return 1};if(typeof data=="function"){data=data()}else{if(typeof data=="string"){var url=data;Jemplate.Ajax.processGet(url,function(data){proc(data)});return null}}return proc(data)};if(typeof Jemplate.Context=="undefined"){Jemplate.Context=function(){}}proto=Jemplate.Context.prototype;proto.include=function(template,args){return this.process(template,args,true)};proto.process=function(template,args,localise){if(localise){this.stash.clone(args)}else{this.stash.update(args)}var func=Jemplate.templateMap[template];if(typeof func=="undefined"){throw ('No Jemplate template named "'+template+'" available')}var output=func(this);if(localise){this.stash.declone()}return output};proto.set_error=function(error,output){this._error=[error,output];return error};proto.plugin=function(name,args){if(typeof name=="undefined"){throw"Unknown plugin name ':"+name+"'"}var func=eval(name);return new func(this,args)};proto.filter=function(text,name,args){if(name=="null"){name="null_filter"}if(typeof this.__filter__.filters[name]=="function"){return this.__filter__.filters[name](text,args,this)}else{throw"Unknown filter name ':"+name+"'"}};if(typeof Jemplate.Plugin=="undefined"){Jemplate.Plugin=function(){}}proto=Jemplate.Plugin.prototype;proto.plugins={};if(typeof Jemplate.Filter=="undefined"){Jemplate.Filter=function(){}}proto=Jemplate.Filter.prototype;proto.filters={};proto.filters.null_filter=function(text){return""};proto.filters.upper=function(text){return text.toUpperCase()};proto.filters.lower=function(text){return text.toLowerCase()};proto.filters.ucfirst=function(text){var first=text.charAt(0);var rest=text.substr(1);return first.toUpperCase()+rest};proto.filters.lcfirst=function(text){var first=text.charAt(0);var rest=text.substr(1);return first.toLowerCase()+rest};proto.filters.trim=function(text){return text.replace(/^\s+/g,"").replace(/\s+$/g,"")};proto.filters.collapse=function(text){return text.replace(/^\s+/g,"").replace(/\s+$/g,"").replace(/\s+/," ")};proto.filters.html=function(text){text=text.replace(/&/g,"&amp;");text=text.replace(/</g,"&lt;");text=text.replace(/>/g,"&gt;");text=text.replace(/"/g,"&quot;");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"<p>\n"+lines.join("\n</p>\n\n<p>\n")+"</p>\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1<br />$1<br />$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1<br />$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i<pad;i++){finalpad+=" "}}else{finalpad=pad}var output=text.replace(/^/gm,finalpad);return output};proto.filters.truncate=function(text,args){var len=args[0];if(!text){return null}if(!len){len=32}if(text.length<len){return text}var newlen=len-3;return text.substr(0,newlen)+"..."};proto.filters.repeat=function(text,iter){if(!text){return null}if(!iter||iter==0){iter=1}if(iter==1){return text}var output=text;for(var i=1;i<iter;i++){output+=text}return output};proto.filters.replace=function(text,args){if(!text){return null}var re_search=args[0];var text_replace=args[1];if(!re_search){re_search=""}if(!text_replace){text_replace=""}var re=new RegExp(re_search,"g");return text.replace(re,text_replace)};if(typeof Jemplate.Stash=="undefined"){Jemplate.Stash=function(stash,config){this.__config__=config;this.data={GLOBAL:this.__config__.SCOPE};this.LOCAL_ANCHOR={};this.data.LOCAL=this.LOCAL_ANCHOR;this.update(stash)}}proto=Jemplate.Stash.prototype;proto.clone=function(args){var data=this.data;this.data={GLOBAL:this.__config__.SCOPE};this.data.LOCAL=this.LOCAL_ANCHOR;this.update(data);this.update(args);this.data._PARENT=data};proto.declone=function(args){this.data=this.data._PARENT||this.data};proto.update=function(args){if(typeof args=="undefined"){return }for(var key in args){if(key!="GLOBAL"&&key!="LOCAL"){this.set(key,args[key])}}};proto.get=function(ident,args){var root=this.data;var value;if((ident instanceof Array)||(typeof ident=="string"&&/\./.test(ident))){if(typeof ident=="string"){ident=ident.split(".");var newIdent=[];for(var i=0;i<ident.length;i++){newIdent.push(ident.replace(/\(.*$/,""));newIdent.push(0)}ident=newIdent}for(var i=0;i<ident.length;i+=2){var dotopArgs=ident.slice(i,i+2);dotopArgs.unshift(root);value=this._dotop.apply(this,dotopArgs);if(typeof value=="undefined"){break}root=value}}else{value=this._dotop(root,ident,args)}if(typeof value=="undefined"||value==null){if(this.__config__.DEBUG_UNDEF){throw ("undefined value found while using DEBUG_UNDEF")}value=""}return value};proto.set=function(ident,value,set_default){var root,result,error;root=this.data;while(true){if((ident instanceof Array)||(typeof ident=="string"&&/\./.test(ident))){if(typeof ident=="string"){ident=ident.split(".");var newIdent=[];for(var i=0;i<ident.length;i++){newIdent.push(ident.replace(/\(.*$/,""));newIdent.push(0)}ident=newIdent}for(var i=0;i<ident.length-2;i+=2){var dotopArgs=ident.slice(i,i+2);dotopArgs.unshift(root);dotopArgs.push(1);result=this._dotop.apply(this,dotopArgs);if(typeof value=="undefined"){break}root=result}var assignArgs=ident.slice(ident.length-2,ident.length);assignArgs.unshift(root);assignArgs.push(value);assignArgs.push(set_default);result=this._assign.apply(this,assignArgs)}else{result=this._assign(root,ident,0,value,set_default)}break}return(typeof result!="undefined")?result:""};proto._dotop=function(root,item,args,lvalue){if(root==this.LOCAL_ANCHOR){root=this.data}var atroot=root==this.data;var value,result=undefined;var is_function_call=args instanceof Array;args=args||[];if(typeof root=="undefined"||typeof item=="undefined"||typeof item=="string"&&item.match(/^[\._]/)){return undefined}if(atroot||(root instanceof Object&&!(root instanceof Array))||root==this.data.GLOBAL){if(typeof root[item]!="undefined"&&root[item]!=null&&(!is_function_call||!this.hash_functions[item])){if(typeof root[item]=="function"){result=root[item].apply(root,args)}else{return root[item]}}else{if(lvalue){return root[item]={}}else{if(this.hash_functions[item]&&!atroot||item=="import"){args.unshift(root);result=this.hash_functions[item].apply(this,args)}else{if(item instanceof Array){result={};for(var i=0;i<item.length;i++){result[item[i]]=root[item[i]]}return result}}}}}else{if(root instanceof Array){if(this.list_functions[item]){args.unshift(root);result=this.list_functions[item].apply(this,args)}else{if(typeof item=="string"&&/^-?\d+$/.test(item)||typeof item=="number"){if(typeof root[item]!="function"){return root[item]}result=root[item].apply(this,args)}else{if(item instanceof Array){for(var i=0;i<item.length;i++){result.push(root[item[i]])}return result}}}}else{if(this.string_functions[item]&&!lvalue){args.unshift(root);result=this.string_functions[item].apply(this,args)}else{if(this.list_functions[item]&&!lvalue){args.unshift([root]);result=this.list_functions[item].apply(this,args)}else{result=undefined}}}}if(result instanceof Array){if(typeof result[0]=="undefined"&&typeof result[1]!="undefined"){throw result[1]}}return result};proto._assign=function(root,item,args,value,set_default){var atroot=root==this.data;var result;args=args||[];if(typeof root=="undefined"||typeof item=="undefined"||typeof item=="string"&&item.match(/^[\._]/)){return undefined}if(atroot||root.constructor==Object||root==this.data.GLOBAL){if(root==this.LOCAL_ANCHOR){root=this.data}if(!(set_default&&typeof root[item]!="undefined")){if(atroot&&item=="GLOBAL"){throw"Attempt to modify GLOBAL access modifier"}if(atroot&&item=="LOCAL"){throw"Attempt to modify LOCAL access modifier"}return root[item]=value}}else{if((root instanceof Array)&&(typeof item=="string"&&/^-?\d+$/.test(item)||typeof item=="number")){if(!(set_default&&typeof root[item]!="undefined")){return root[item]=value}}else{if((root.constructor!=Object)&&(root instanceof Object)){try{result=root[item].apply(root,args)}catch(e){}}else{throw"dont know how to assign to ["+root+"."+item+"]"}}}return undefined};proto.string_functions={};proto.string_functions["typeof"]=function(value){return typeof value};proto.string_functions.chunk=function(string,size){var list=new Array();if(!size){size=1}if(size<0){size=0-size;for(var i=string.length-size;i>=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i<string.length;i=i+size){list.push(string.substr(i,size))}}return list};proto.string_functions.defined=function(string){return 1};proto.string_functions.hash=function(string){return{"value":string}};proto.string_functions.length=function(string){return string.length};proto.string_functions.list=function(string){return[string]};proto.string_functions.match=function(string,re,modifiers){var regexp=new RegExp(re,modifiers==undefined?"g":modifiers);var list=string.match(regexp);return list};proto.string_functions.repeat=function(string,args){var n=args||1;var output="";for(var i=0;i<n;i++){output+=string}return output};proto.string_functions.replace=function(string,re,sub,modifiers){var regexp=new RegExp(re,modifiers==undefined?"g":modifiers);if(!sub){sub=""}return string.replace(regexp,sub)};proto.string_functions.search=function(string,re){var regexp=new RegExp(re);return(string.search(regexp)>=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i<list.length;i++){if(list[i].match(regexp)){result.push(list[i])}}return result};proto.list_functions.unique=function(list){var result=[];var seen={};for(var i=0;i<list.length;i++){var elem=list[i];if(!seen[elem]){result.push(elem)}seen[elem]=true}return result};proto.list_functions.reverse=function(list){var result=[];for(var i=list.length-1;i>=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j<elem.length;j++){result.push(elem[j])}}else{result.push(elem)}};push_all(list);for(var i=1;i<arguments.length;i++){push_all(arguments[i])}return result};proto.list_functions.slice=function(list,start,end){if(end==-1){return list.slice(start)}return list.slice(start,end+1)};proto.list_functions.splice=function(list){var args=Array.prototype.slice.call(arguments);args.shift();return list.splice.apply(list,args)};proto.list_functions.push=function(list,value){list.push(value);return list};proto.list_functions.pop=function(list){return list.pop()};proto.list_functions.unshift=function(list,value){list.unshift(value);return list};proto.list_functions.shift=function(list){return list.shift()};proto.list_functions.first=function(list){return list[0]};proto.list_functions.size=function(list){return list.length};proto.list_functions.max=function(list){return list.length-1};proto.list_functions.last=function(list){return list.slice(-1)};proto.hash_functions={};proto.hash_functions["typeof"]=function(hash){return"object"};proto.hash_functions.each=function(hash){var list=new Array();for(var key in hash){list.push(key,hash[key])}return list};proto.hash_functions.exists=function(hash,key){return(typeof (hash[key])=="undefined")?0:1};proto.hash_functions["import"]=function(hash,hash2){for(var key in hash2){hash[key]=hash2[key]}return""};proto.hash_functions.keys=function(hash){var list=new Array();for(var key in hash){list.push(key)}return list};proto.hash_functions.list=function(hash,what){var list=new Array();var key;if(what=="keys"){for(key in hash){list.push(key)}}else{if(what=="values"){for(key in hash){list.push(hash[key])}}else{if(what=="each"){for(key in hash){list.push(key,hash[key])}}else{for(key in hash){list.push({"key":key,"value":hash[key]})}}}}return list};proto.hash_functions.nsort=function(hash){var list=new Array();for(var key in hash){list.push(key)}return list.sort(function(a,b){return(a-b)})};proto.hash_functions.item=function(hash,key){return hash[key]};proto.hash_functions.size=function(hash){var size=0;for(var key in hash){size++}return size};proto.hash_functions.sort=function(hash){var list=new Array();for(var key in hash){list.push(key)}return list.sort()};proto.hash_functions.values=function(hash){var list=new Array();for(var key in hash){list.push(hash[key])}return list};proto.hash_functions.pairs=function(hash){var list=new Array();var keys=new Array();for(var key in hash){keys.push(key)}keys.sort();for(var key in keys){key=keys[key];list.push({"key":key,"value":hash[key]})}return list};proto.hash_functions.remove=function(hash,key){return delete hash[key]};proto.hash_functions["delete"]=proto.hash_functions.remove;if(typeof Jemplate.Iterator=="undefined"){Jemplate.Iterator=function(object){if(object instanceof Array){this.object=object;this.size=object.length;this.max=this.size-1}else{if(object instanceof Object){this.object=object;var object_keys=new Array;for(var key in object){object_keys[object_keys.length]=key}this.object_keys=object_keys.sort();this.size=object_keys.length;this.max=this.size-1}else{if(typeof object=="undefined"||object==null||object==""){this.object=null;this.max=-1}}}}}proto=Jemplate.Iterator.prototype;proto.get_first=function(){this.index=0;this.first=1;this.last=0;this.count=1;return this.get_next(1)};proto.get_next=function(should_init){var object=this.object;var index;if(typeof (should_init)!="undefined"&&should_init){index=this.index}else{index=++this.index;this.first=0;this.count=this.index+1;if(this.index==this.size-1){this.last=1}}if(typeof object=="undefined"){throw ("No object to iterate")}if(this.object_keys){if(index<this.object_keys.length){this.prev=index>0?this.object_keys[index-1]:"";this.next=index<this.max?this.object_keys[index+1]:"";return[this.object_keys[index],false]}}else{if(index<=this.max){this.prev=index>0?object[index-1]:"";this.next=index<this.max?object[index+1]:"";return[object[index],false]}}return[null,true]};var stubExplanation="stub that doesn't do anything. Try including the jQuery, YUI, or XHR option when building the runtime";Jemplate.Ajax={get:function(url,callback){throw ("This is a Jemplate.Ajax.get "+stubExplanation)},processGet:function(url,callback){throw ("This is a Jemplate.Ajax.processGet "+stubExplanation)},post:function(url,callback){throw ("This is a Jemplate.Ajax.post "+stubExplanation)}};Jemplate.JSON={parse:function(decodeValue){throw ("This is a Jemplate.JSON.parse "+stubExplanation)},stringify:function(encodeValue){throw ("This is a Jemplate.JSON.stringify "+stubExplanation)}}}())
 ...
 }
 
@@ -19297,7 +19820,7 @@ sub json_json2_internal {
 
 var JSON;
 
-if(!JSON){var JSON}if(!JSON){JSON=function(){function f(n){return n<10?"0"+n:n}Date.prototype.toJSON=function(){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){return escapeable.test(string)?'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==="string"){return c}c=a.charCodeAt();return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(typeof value.length==="number"&&!(value.propertyIsEnumerable("length"))){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return null}return{stringify:function(value,replacer,space){var i;gap="";indent="";if(space){if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}}if(!replacer){rep=function(key,value){if(!Object.hasOwnProperty.call(this,key)){return undefined}return value}}else{if(typeof replacer==="function"||(typeof replacer==="object"&&typeof replacer.length==="number")){rep=replacer}else{throw new Error("JSON.stringify")}}return str("",{"":value})},parse:function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}if(/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")},quote:quote}}()}
+if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return""}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}())
 
 Jemplate.JSON={parse:function(A){return JSON.parse(A)},stringify:function(A){return JSON.stringify(A)}}
 
@@ -19313,7 +19836,7 @@ sub json_yui {
 
 sub json2 {
     <<'...';
-if(!JSON){var JSON}if(!JSON){JSON=function(){function f(n){return n<10?"0"+n:n}Date.prototype.toJSON=function(){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){return escapeable.test(string)?'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==="string"){return c}c=a.charCodeAt();return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(typeof value.length==="number"&&!(value.propertyIsEnumerable("length"))){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return null}return{stringify:function(value,replacer,space){var i;gap="";indent="";if(space){if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}}if(!replacer){rep=function(key,value){if(!Object.hasOwnProperty.call(this,key)){return undefined}return value}}else{if(typeof replacer==="function"||(typeof replacer==="object"&&typeof replacer.length==="number")){rep=replacer}else{throw new Error("JSON.stringify")}}return str("",{"":value})},parse:function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}if(/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")},quote:quote}}()}
+if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return""}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}())
 ...
 }
 
@@ -19352,7 +19875,8 @@ use warnings;
 use Template 2.14;
 use Getopt::Long;
 
-our $VERSION = '0.22';
+ 
+our $VERSION = '0.260';
 
 use Jemplate::Parser;
 
@@ -19423,7 +19947,7 @@ sub main {
     my ($runtime, $compile, $list) = @$jemplate_options{qw/runtime compile list/};
 
     if ($runtime) {
-        runtime_source_code(@$jemplate_options{qw/runtime ajax json xhr xxx compact/});
+        print runtime_source_code(@$jemplate_options{qw/runtime ajax json xhr xxx compact/});
         return unless $compile;
     }
 
@@ -19565,8 +20089,10 @@ sub recurse_dir {
     my $dir = shift;
     my @files;
     foreach ( File::Find::Rule->file->in( $dir ) ) {
-        # don't include .hidden files
-        unless ($_ =~ '\/\.') { push(@files, $_); }
+        if ( m{/\.[^\.]+} ) {} # Skip ".hidden" files or directories
+        else {
+            push @files, $_;
+        }
     }
     return @files;
 }
@@ -19578,7 +20104,7 @@ sub make_file_list {
 
     foreach my $arg (@args) {
         unless (-e $arg) { next; } # file exists
-        unless (-s $arg) { next; } # file size > 0
+        unless (-s $arg or -d $arg) { next; } # file size > 0 or directory (for Win platform)
 
         if (-d $arg) {
             foreach my $full ( recurse_dir($arg) ) {
@@ -19608,6 +20134,8 @@ sub runtime_source_code {
     require Jemplate::Runtime;
     require Jemplate::Runtime::Compact;
 
+    unshift @_, "standard" unless @_;
+
     my ($runtime, $ajax, $json, $xhr, $xxx, $compact) = map { defined $_ ? lc $_ : "" } @_[0 .. 5];
 
     my $Jemplate_Runtime = $compact ? "Jemplate::Runtime::Compact" : "Jemplate::Runtime";
@@ -19657,7 +20185,7 @@ sub runtime_source_code {
 
     push @runtime, $Jemplate_Runtime->xxx if $xxx;
 
-    print join ";", @runtime;
+    return join ";", @runtime;
 }
 
 
@@ -19728,7 +20256,7 @@ sub _preamble {
    Template Toolkit. Any changes made to this file will be lost the next
    time the templates are compiled.
 
-   Copyright 2006 - Ingy döt Net - All rights reserved.
+   Copyright 2006-2008 - Ingy döt Net - All rights reserved.
 */
 
 if (typeof(Jemplate) == 'undefined')
@@ -1,7 +1,11 @@
 #line 1
 package Module::Install::Base;
 
-$VERSION = '0.71';
+use strict 'vars';
+use vars qw{$VERSION};
+BEGIN {
+	$VERSION = '0.91';
+}
 
 # Suspend handler for "redefined" warnings
 BEGIN {
@@ -9,52 +13,56 @@ BEGIN {
 	$SIG{__WARN__} = sub { $w };
 }
 
-### This is the ONLY module that shouldn't have strict on
-# use strict;
-
-#line 41
+#line 42
 
 sub new {
-    my ($class, %args) = @_;
-
-    foreach my $method ( qw(call load) ) {
-        *{"$class\::$method"} = sub {
-            shift()->_top->$method(@_);
-        } unless defined &{"$class\::$method"};
-    }
-
-    bless( \%args, $class );
+	my $class = shift;
+	unless ( defined &{"${class}::call"} ) {
+		*{"${class}::call"} = sub { shift->_top->call(@_) };
+	}
+	unless ( defined &{"${class}::load"} ) {
+		*{"${class}::load"} = sub { shift->_top->load(@_) };
+	}
+	bless { @_ }, $class;
 }
 
 #line 61
 
 sub AUTOLOAD {
-    my $self = shift;
-    local $@;
-    my $autoload = eval { $self->_top->autoload } or return;
-    goto &$autoload;
+	local $@;
+	my $func = eval { shift->_top->autoload } or return;
+	goto &$func;
 }
 
-#line 76
+#line 75
 
-sub _top { $_[0]->{_top} }
+sub _top {
+	$_[0]->{_top};
+}
 
-#line 89
+#line 90
 
 sub admin {
-    $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new;
+	$_[0]->_top->{admin}
+	or
+	Module::Install::Base::FakeAdmin->new;
 }
 
+#line 106
+
 sub is_admin {
-    $_[0]->admin->VERSION;
+	$_[0]->admin->VERSION;
 }
 
 sub DESTROY {}
 
 package Module::Install::Base::FakeAdmin;
 
-my $Fake;
-sub new { $Fake ||= bless(\@_, $_[0]) }
+my $fake;
+
+sub new {
+	$fake ||= bless(\@_, $_[0]);
+}
 
 sub AUTOLOAD {}
 
@@ -67,4 +75,4 @@ BEGIN {
 
 1;
 
-#line 138
+#line 154
@@ -2,18 +2,16 @@
 package Module::Install::Can;
 
 use strict;
-use Module::Install::Base;
-use Config ();
-### This adds a 5.005 Perl version dependency.
-### This is a bug and will be fixed.
-use File::Spec ();
-use ExtUtils::MakeMaker ();
-
-use vars qw{$VERSION $ISCORE @ISA};
+use Config                ();
+use File::Spec            ();
+use ExtUtils::MakeMaker   ();
+use Module::Install::Base ();
+
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 # check if we can load some module
@@ -39,6 +37,7 @@ sub can_run {
 	return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
 
 	for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+		next if $dir eq '';
 		my $abs = File::Spec->catfile($dir, $_[1]);
 		return $abs if (-x $abs or $abs = MM->maybe_command($abs));
 	}
@@ -79,4 +78,4 @@ if ( $^O eq 'cygwin' ) {
 
 __END__
 
-#line 157
+#line 156
@@ -2,24 +2,24 @@
 package Module::Install::Fetch;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub get_file {
     my ($self, %args) = @_;
-    my ($scheme, $host, $path, $file) = 
+    my ($scheme, $host, $path, $file) =
         $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
 
     if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
         $args{url} = $args{ftp_url}
             or (warn("LWP support unavailable!\n"), return);
-        ($scheme, $host, $path, $file) = 
+        ($scheme, $host, $path, $file) =
             $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
     }
 
@@ -2,13 +2,13 @@
 package Module::Install::Include;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub include {
@@ -2,14 +2,14 @@
 package Module::Install::Makefile;
 
 use strict 'vars';
-use Module::Install::Base;
-use ExtUtils::MakeMaker ();
+use ExtUtils::MakeMaker   ();
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub Makefile { $_[0] }
@@ -36,9 +36,9 @@ sub prompt {
 
 sub makemaker_args {
 	my $self = shift;
-	my $args = ($self->{makemaker_args} ||= {});
-	  %$args = ( %$args, @_ ) if @_;
-	$args;
+	my $args = ( $self->{makemaker_args} ||= {} );
+	%$args = ( %$args, @_ );
+	return $args;
 }
 
 # For mm args that take multiple space-seperated args,
@@ -64,7 +64,7 @@ sub clean_files {
 	my $self  = shift;
 	my $clean = $self->makemaker_args->{clean} ||= {};
 	  %$clean = (
-		%$clean, 
+		%$clean,
 		FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_),
 	);
 }
@@ -73,7 +73,7 @@ sub realclean_files {
 	my $self      = shift;
 	my $realclean = $self->makemaker_args->{realclean} ||= {};
 	  %$realclean = (
-		%$realclean, 
+		%$realclean,
 		FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_),
 	);
 }
@@ -114,11 +114,32 @@ sub write {
 	my $self = shift;
 	die "&Makefile->write() takes no arguments\n" if @_;
 
-	# Make sure we have a new enough
+	# Check the current Perl version
+	my $perl_version = $self->perl_version;
+	if ( $perl_version ) {
+		eval "use $perl_version; 1"
+			or die "ERROR: perl: Version $] is installed, "
+			. "but we need version >= $perl_version";
+	}
+
+	# Make sure we have a new enough MakeMaker
 	require ExtUtils::MakeMaker;
-	$self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION );
 
-	# Generate the 
+	if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) {
+		# MakeMaker can complain about module versions that include
+		# an underscore, even though its own version may contain one!
+		# Hence the funny regexp to get rid of it.  See RT #35800
+		# for details.
+		$self->build_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ );
+		$self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ );
+	} else {
+		# Allow legacy-compatibility with 5.005 by depending on the
+		# most recent EU:MM that supported 5.005.
+		$self->build_requires( 'ExtUtils::MakeMaker' => 6.42 );
+		$self->configure_requires( 'ExtUtils::MakeMaker' => 6.42 );
+	}
+
+	# Generate the MakeMaker params
 	my $args = $self->makemaker_args;
 	$args->{DISTNAME} = $self->name;
 	$args->{NAME}     = $self->module_name || $self->name;
@@ -127,7 +148,7 @@ sub write {
 	if ( $self->tests ) {
 		$args->{test} = { TESTS => $self->tests };
 	}
-	if ($] >= 5.005) {
+	if ( $] >= 5.005 ) {
 		$args->{ABSTRACT} = $self->abstract;
 		$args->{AUTHOR}   = $self->author;
 	}
@@ -141,7 +162,7 @@ sub write {
 		delete $args->{SIGN};
 	}
 
-	# merge both kinds of requires into prereq_pm
+	# Merge both kinds of requires into prereq_pm
 	my $prereq = ($args->{PREREQ_PM} ||= {});
 	%$prereq = ( %$prereq,
 		map { @$_ }
@@ -175,7 +196,9 @@ sub write {
 
 	my $user_preop = delete $args{dist}->{PREOP};
 	if (my $preop = $self->admin->preop($user_preop)) {
-		$args{dist} = $preop;
+		foreach my $key ( keys %$preop ) {
+			$args{dist}->{$key} = $preop->{$key};
+		}
 	}
 
 	my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
@@ -188,7 +211,7 @@ sub fix_up_makefile {
 	my $top_class     = ref($self->_top) || '';
 	my $top_version   = $self->_top->VERSION || '';
 
-	my $preamble = $self->preamble 
+	my $preamble = $self->preamble
 		? "# Preamble by $top_class $top_version\n"
 			. $self->preamble
 		: '';
@@ -242,4 +265,4 @@ sub postamble {
 
 __END__
 
-#line 371
+#line 394
@@ -2,24 +2,26 @@
 package Module::Install::Metadata;
 
 use strict 'vars';
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
+my @boolean_keys = qw{
+	sign
+};
+
 my @scalar_keys = qw{
 	name
 	module_name
 	abstract
 	author
 	version
-	license
 	distribution_type
-	perl_version
 	tests
 	installdirs
 };
@@ -30,108 +32,202 @@ my @tuple_keys = qw{
 	requires
 	recommends
 	bundles
+	resources
+};
+
+my @resource_keys = qw{
+	homepage
+	bugtracker
+	repository
+};
+
+my @array_keys = qw{
+	keywords
 };
 
-sub Meta            { shift        }
-sub Meta_ScalarKeys { @scalar_keys }
-sub Meta_TupleKeys  { @tuple_keys  }
+sub Meta              { shift          }
+sub Meta_BooleanKeys  { @boolean_keys  }
+sub Meta_ScalarKeys   { @scalar_keys   }
+sub Meta_TupleKeys    { @tuple_keys    }
+sub Meta_ResourceKeys { @resource_keys }
+sub Meta_ArrayKeys    { @array_keys    }
 
-foreach my $key (@scalar_keys) {
+foreach my $key ( @boolean_keys ) {
 	*$key = sub {
 		my $self = shift;
-		return $self->{values}{$key} if defined wantarray and !@_;
-		$self->{values}{$key} = shift;
+		if ( defined wantarray and not @_ ) {
+			return $self->{values}->{$key};
+		}
+		$self->{values}->{$key} = ( @_ ? $_[0] : 1 );
 		return $self;
 	};
 }
 
-sub requires {
-	my $self = shift;
-	while ( @_ ) {
-		my $module  = shift or last;
-		my $version = shift || 0;
-		push @{ $self->{values}->{requires} }, [ $module, $version ];
-	}
-	$self->{values}{requires};
+foreach my $key ( @scalar_keys ) {
+	*$key = sub {
+		my $self = shift;
+		return $self->{values}->{$key} if defined wantarray and !@_;
+		$self->{values}->{$key} = shift;
+		return $self;
+	};
 }
 
-sub build_requires {
-	my $self = shift;
-	while ( @_ ) {
-		my $module  = shift or last;
-		my $version = shift || 0;
-		push @{ $self->{values}->{build_requires} }, [ $module, $version ];
-	}
-	$self->{values}{build_requires};
+foreach my $key ( @array_keys ) {
+	*$key = sub {
+		my $self = shift;
+		return $self->{values}->{$key} if defined wantarray and !@_;
+		$self->{values}->{$key} ||= [];
+		push @{$self->{values}->{$key}}, @_;
+		return $self;
+	};
 }
 
-sub configure_requires {
-	my $self = shift;
-	while ( @_ ) {
-		my $module  = shift or last;
-		my $version = shift || 0;
-		push @{ $self->{values}->{configure_requires} }, [ $module, $version ];
-	}
-	$self->{values}{configure_requires};
+foreach my $key ( @resource_keys ) {
+	*$key = sub {
+		my $self = shift;
+		unless ( @_ ) {
+			return () unless $self->{values}->{resources};
+			return map  { $_->[1] }
+			       grep { $_->[0] eq $key }
+			       @{ $self->{values}->{resources} };
+		}
+		return $self->{values}->{resources}->{$key} unless @_;
+		my $uri = shift or die(
+			"Did not provide a value to $key()"
+		);
+		$self->resources( $key => $uri );
+		return 1;
+	};
 }
 
-sub recommends {
-	my $self = shift;
-	while ( @_ ) {
-		my $module  = shift or last;
-		my $version = shift || 0;
-		push @{ $self->{values}->{recommends} }, [ $module, $version ];
-	}
-	$self->{values}{recommends};
+foreach my $key ( grep { $_ ne "resources" } @tuple_keys) {
+	*$key = sub {
+		my $self = shift;
+		return $self->{values}->{$key} unless @_;
+		my @added;
+		while ( @_ ) {
+			my $module  = shift or last;
+			my $version = shift || 0;
+			push @added, [ $module, $version ];
+		}
+		push @{ $self->{values}->{$key} }, @added;
+		return map {@$_} @added;
+	};
 }
 
-sub bundles {
+# Resource handling
+my %lc_resource = map { $_ => 1 } qw{
+	homepage
+	license
+	bugtracker
+	repository
+};
+
+sub resources {
 	my $self = shift;
 	while ( @_ ) {
-		my $module  = shift or last;
-		my $version = shift || 0;
-		push @{ $self->{values}->{bundles} }, [ $module, $version ];
+		my $name  = shift or last;
+		my $value = shift or next;
+		if ( $name eq lc $name and ! $lc_resource{$name} ) {
+			die("Unsupported reserved lowercase resource '$name'");
+		}
+		$self->{values}->{resources} ||= [];
+		push @{ $self->{values}->{resources} }, [ $name, $value ];
 	}
-	$self->{values}{bundles};
+	$self->{values}->{resources};
 }
 
 # Aliases for build_requires that will have alternative
 # meanings in some future version of META.yml.
-sub test_requires      { shift->build_requires(@_) }
-sub install_requires   { shift->build_requires(@_) }
+sub test_requires     { shift->build_requires(@_) }
+sub install_requires  { shift->build_requires(@_) }
 
 # Aliases for installdirs options
-sub install_as_core    { $_[0]->installdirs('perl')   }
-sub install_as_cpan    { $_[0]->installdirs('site')   }
-sub install_as_site    { $_[0]->installdirs('site')   }
-sub install_as_vendor  { $_[0]->installdirs('vendor') }
-
-sub sign {
-	my $self = shift;
-	return $self->{'values'}{'sign'} if defined wantarray and ! @_;
-	$self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
-	return $self;
-}
+sub install_as_core   { $_[0]->installdirs('perl')   }
+sub install_as_cpan   { $_[0]->installdirs('site')   }
+sub install_as_site   { $_[0]->installdirs('site')   }
+sub install_as_vendor { $_[0]->installdirs('vendor') }
 
 sub dynamic_config {
 	my $self = shift;
 	unless ( @_ ) {
-		warn "You MUST provide an explicit true/false value to dynamic_config, skipping\n";
+		warn "You MUST provide an explicit true/false value to dynamic_config\n";
 		return $self;
 	}
-	$self->{values}{dynamic_config} = $_[0] ? 1 : 0;
-	return $self;
+	$self->{values}->{dynamic_config} = $_[0] ? 1 : 0;
+	return 1;
+}
+
+sub perl_version {
+	my $self = shift;
+	return $self->{values}->{perl_version} unless @_;
+	my $version = shift or die(
+		"Did not provide a value to perl_version()"
+	);
+
+	# Normalize the version
+	$version = $self->_perl_version($version);
+
+	# We don't support the reall old versions
+	unless ( $version >= 5.005 ) {
+		die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n";
+	}
+
+	$self->{values}->{perl_version} = $version;
+}
+
+#Stolen from M::B
+my %license_urls = (
+    perl         => 'http://dev.perl.org/licenses/',
+    apache       => 'http://apache.org/licenses/LICENSE-2.0',
+    artistic     => 'http://opensource.org/licenses/artistic-license.php',
+    artistic_2   => 'http://opensource.org/licenses/artistic-license-2.0.php',
+    lgpl         => 'http://opensource.org/licenses/lgpl-license.php',
+    lgpl2        => 'http://opensource.org/licenses/lgpl-2.1.php',
+    lgpl3        => 'http://opensource.org/licenses/lgpl-3.0.html',
+    bsd          => 'http://opensource.org/licenses/bsd-license.php',
+    gpl          => 'http://opensource.org/licenses/gpl-license.php',
+    gpl2         => 'http://opensource.org/licenses/gpl-2.0.php',
+    gpl3         => 'http://opensource.org/licenses/gpl-3.0.html',
+    mit          => 'http://opensource.org/licenses/mit-license.php',
+    mozilla      => 'http://opensource.org/licenses/mozilla1.1.php',
+    open_source  => undef,
+    unrestricted => undef,
+    restrictive  => undef,
+    unknown      => undef,
+);
+
+sub license {
+	my $self = shift;
+	return $self->{values}->{license} unless @_;
+	my $license = shift or die(
+		'Did not provide a value to license()'
+	);
+	$self->{values}->{license} = $license;
+
+	# Automatically fill in license URLs
+	if ( $license_urls{$license} ) {
+		$self->resources( license => $license_urls{$license} );
+	}
+
+	return 1;
 }
 
 sub all_from {
 	my ( $self, $file ) = @_;
 
 	unless ( defined($file) ) {
-		my $name = $self->name
-			or die "all_from called with no args without setting name() first";
+		my $name = $self->name or die(
+			"all_from called with no args without setting name() first"
+		);
 		$file = join('/', 'lib', split(/-/, $name)) . '.pm';
 		$file =~ s{.*/}{} unless -e $file;
-		die "all_from: cannot find $file from $name" unless -e $file;
+		unless ( -e $file ) {
+			die("all_from cannot find $file from $name");
+		}
+	}
+	unless ( -f $file ) {
+		die("The path '$file' does not exist, or is not a file");
 	}
 
 	# Some methods pull from POD instead of code.
@@ -153,7 +249,7 @@ sub all_from {
 
 sub provides {
 	my $self     = shift;
-	my $provides = ( $self->{values}{provides} ||= {} );
+	my $provides = ( $self->{values}->{provides} ||= {} );
 	%$provides = (%$provides, @_) if @_;
 	return $provides;
 }
@@ -182,7 +278,7 @@ sub auto_provides {
 sub feature {
 	my $self     = shift;
 	my $name     = shift;
-	my $features = ( $self->{values}{features} ||= [] );
+	my $features = ( $self->{values}->{features} ||= [] );
 	my $mods;
 
 	if ( @_ == 1 and ref( $_[0] ) ) {
@@ -218,8 +314,8 @@ sub features {
 sub no_index {
 	my $self = shift;
 	my $type = shift;
-	push @{ $self->{values}{no_index}{$type} }, @_ if $type;
-	return $self->{values}{no_index};
+	push @{ $self->{values}->{no_index}->{$type} }, @_ if $type;
+	return $self->{values}->{no_index};
 }
 
 sub read {
@@ -267,22 +363,25 @@ sub abstract_from {
 	 );
 }
 
+# Add both distribution and module name
 sub name_from {
-	my $self = shift;
+	my ($self, $file) = @_;
 	if (
-		Module::Install::_read($_[0]) =~ m/
-		^ \s
+		Module::Install::_read($file) =~ m/
+		^ \s*
 		package \s*
 		([\w:]+)
 		\s* ;
 		/ixms
 	) {
-		my $name = $1;
+		my ($name, $module_name) = ($1, $1);
 		$name =~ s{::}{-}g;
 		$self->name($name);
+		unless ( $self->module_name ) {
+			$self->module_name($module_name);
+		}
 	} else {
-		die "Cannot determine name from $_[0]\n";
-		return;
+		die("Cannot determine name from $file\n");
 	}
 }
 
@@ -291,7 +390,7 @@ sub perl_version_from {
 	if (
 		Module::Install::_read($_[0]) =~ m/
 		^
-		use \s*
+		(?:use|require) \s*
 		v?
 		([\d_\.]+)
 		\s* ;
@@ -340,24 +439,25 @@ sub license_from {
 	/ixms ) {
 		my $license_text = $1;
 		my @phrases      = (
-			'under the same (?:terms|license) as perl itself' => 'perl',        1,
-			'GNU public license'                              => 'gpl',         1,
-			'GNU lesser public license'                       => 'lgpl',        1,
-			'BSD license'                                     => 'bsd',         1,
-			'Artistic license'                                => 'artistic',    1,
-			'GPL'                                             => 'gpl',         1,
-			'LGPL'                                            => 'lgpl',        1,
-			'BSD'                                             => 'bsd',         1,
-			'Artistic'                                        => 'artistic',    1,
-			'MIT'                                             => 'mit',         1,
-			'proprietary'                                     => 'proprietary', 0,
+			'under the same (?:terms|license) as (?:perl|the perl programming language) itself' => 'perl', 1,
+			'GNU general public license'         => 'gpl',         1,
+			'GNU public license'                 => 'gpl',         1,
+			'GNU lesser general public license'  => 'lgpl',        1,
+			'GNU lesser public license'          => 'lgpl',        1,
+			'GNU library general public license' => 'lgpl',        1,
+			'GNU library public license'         => 'lgpl',        1,
+			'BSD license'                        => 'bsd',         1,
+			'Artistic license'                   => 'artistic',    1,
+			'GPL'                                => 'gpl',         1,
+			'LGPL'                               => 'lgpl',        1,
+			'BSD'                                => 'bsd',         1,
+			'Artistic'                           => 'artistic',    1,
+			'MIT'                                => 'mit',         1,
+			'proprietary'                        => 'proprietary', 0,
 		);
 		while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
 			$pattern =~ s{\s+}{\\s+}g;
 			if ( $license_text =~ /\b$pattern\b/i ) {
-				if ( $osi and $license_text =~ /All rights reserved/i ) {
-					warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
-				}
 				$self->license($license);
 				return 1;
 			}
@@ -368,4 +468,157 @@ sub license_from {
 	return 'unknown';
 }
 
+sub _extract_bugtracker {
+	my @links   = $_[0] =~ m#L<(\Qhttp://rt.cpan.org/\E[^>]+)>#g;
+	my %links;
+	@links{@links}=();
+	@links=keys %links;
+	return @links;
+}
+
+sub bugtracker_from {
+	my $self    = shift;
+	my $content = Module::Install::_read($_[0]);
+	my @links   = _extract_bugtracker($content);
+	unless ( @links ) {
+		warn "Cannot determine bugtracker info from $_[0]\n";
+		return 0;
+	}
+	if ( @links > 1 ) {
+		warn "Found more than on rt.cpan.org link in $_[0]\n";
+		return 0;
+	}
+
+	# Set the bugtracker
+	bugtracker( $links[0] );
+	return 1;
+}
+
+sub requires_from {
+	my $self     = shift;
+	my $content  = Module::Install::_readperl($_[0]);
+	my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg;
+	while ( @requires ) {
+		my $module  = shift @requires;
+		my $version = shift @requires;
+		$self->requires( $module => $version );
+	}
+}
+
+sub test_requires_from {
+	my $self     = shift;
+	my $content  = Module::Install::_readperl($_[0]);
+	my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg;
+	while ( @requires ) {
+		my $module  = shift @requires;
+		my $version = shift @requires;
+		$self->test_requires( $module => $version );
+	}
+}
+
+# Convert triple-part versions (eg, 5.6.1 or 5.8.9) to
+# numbers (eg, 5.006001 or 5.008009).
+# Also, convert double-part versions (eg, 5.8)
+sub _perl_version {
+	my $v = $_[-1];
+	$v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e;
+	$v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e;
+	$v =~ s/(\.\d\d\d)000$/$1/;
+	$v =~ s/_.+$//;
+	if ( ref($v) ) {
+		# Numify
+		$v = $v + 0;
+	}
+	return $v;
+}
+
+
+
+
+
+######################################################################
+# MYMETA Support
+
+sub WriteMyMeta {
+	die "WriteMyMeta has been deprecated";
+}
+
+sub write_mymeta_yaml {
+	my $self = shift;
+
+	# We need YAML::Tiny to write the MYMETA.yml file
+	unless ( eval { require YAML::Tiny; 1; } ) {
+		return 1;
+	}
+
+	# Generate the data
+	my $meta = $self->_write_mymeta_data or return 1;
+
+	# Save as the MYMETA.yml file
+	print "Writing MYMETA.yml\n";
+	YAML::Tiny::DumpFile('MYMETA.yml', $meta);
+}
+
+sub write_mymeta_json {
+	my $self = shift;
+
+	# We need JSON to write the MYMETA.json file
+	unless ( eval { require JSON; 1; } ) {
+		return 1;
+	}
+
+	# Generate the data
+	my $meta = $self->_write_mymeta_data or return 1;
+
+	# Save as the MYMETA.yml file
+	print "Writing MYMETA.json\n";
+	Module::Install::_write(
+		'MYMETA.json',
+		JSON->new->pretty(1)->canonical->encode($meta),
+	);
+}
+
+sub _write_mymeta_data {
+	my $self = shift;
+
+	# If there's no existing META.yml there is nothing we can do
+	return undef unless -f 'META.yml';
+
+	# We need Parse::CPAN::Meta to load the file
+	unless ( eval { require Parse::CPAN::Meta; 1; } ) {
+		return undef;
+	}
+
+	# Merge the perl version into the dependencies
+	my $val  = $self->Meta->{values};
+	my $perl = delete $val->{perl_version};
+	if ( $perl ) {
+		$val->{requires} ||= [];
+		my $requires = $val->{requires};
+
+		# Canonize to three-dot version after Perl 5.6
+		if ( $perl >= 5.006 ) {
+			$perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e
+		}
+		unshift @$requires, [ perl => $perl ];
+	}
+
+	# Load the advisory META.yml file
+	my @yaml = Parse::CPAN::Meta::LoadFile('META.yml');
+	my $meta = $yaml[0];
+
+	# Overwrite the non-configure dependency hashs
+	delete $meta->{requires};
+	delete $meta->{build_requires};
+	delete $meta->{recommends};
+	if ( exists $val->{requires} ) {
+		$meta->{requires} = { map { @$_ } @{ $val->{requires} } };
+	}
+	if ( exists $val->{build_requires} ) {
+		$meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } };
+	}
+
+	return $meta;
+}
+
 1;
@@ -1,50 +1,29 @@
 #line 1
 package Module::Install::Scripts;
 
-use strict;
-use Module::Install::Base;
-use File::Basename ();
+use strict 'vars';
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
-}
-
-sub prompt_script {
-    my ($self, $script_file) = @_;
-
-    my ($prompt, $abstract, $default);
-    foreach my $line ( $self->_read_script($script_file) ) {
-        last unless $line =~ /^#/;
-        $prompt = $1   if $line =~ /^#\s*prompt:\s+(.*)/;
-        $default = $1  if $line =~ /^#\s*default:\s+(.*)/;
-        $abstract = $1 if $line =~ /^#\s*abstract:\s+(.*)/;
-    }
-    unless (defined $prompt) {
-        my $script_name = File::Basename::basename($script_file);
-        $prompt = "Do you want to install '$script_name'";
-        $prompt .= " ($abstract)" if defined $abstract;
-        $prompt .= '?';
-    }
-    return unless $self->prompt($prompt, ($default || 'n')) =~ /^[Yy]/;
-    $self->install_script($script_file);
 }
 
 sub install_script {
-    my $self = shift;
-    my $args = $self->makemaker_args;
-    my $exe_files = $args->{EXE_FILES} ||= [];
-    push @$exe_files, @_;
-}
-
-sub _read_script {
-    my ($self, $script_file) = @_;
-    local *SCRIPT;
-    open SCRIPT, $script_file
-      or die "Can't open '$script_file' for input: $!\n";
-    return <SCRIPT>;
+	my $self = shift;
+	my $args = $self->makemaker_args;
+	my $exe  = $args->{EXE_FILES} ||= [];
+        foreach ( @_ ) {
+		if ( -f $_ ) {
+			push @$exe, $_;
+		} elsif ( -d 'script' and -f "script/$_" ) {
+			push @$exe, "script/$_";
+		} else {
+			die("Cannot find script '$_'");
+		}
+	}
 }
 
 1;
@@ -12,15 +12,18 @@ BEGIN {
 }
 
 sub use_test_base {
-    my $self = shift; 
+    my $self = shift;
     $self->include('Test::Base');
     $self->include('Test::Base::Filter');
     $self->include('Spiffy');
     $self->include('Test::More');
     $self->include('Test::Builder');
     $self->include('Test::Builder::Module');
+    $self->requires('Filter::Util::Call');
 }
 
 1;
 
-#line 67
+=encoding utf8
+
+#line 70
@@ -2,12 +2,12 @@
 package Module::Install::Win32;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
-	@ISA     = qw{Module::Install::Base};
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
 
@@ -2,11 +2,11 @@
 package Module::Install::WriteAll;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.71';
+	$VERSION = '0.91';;
 	@ISA     = qw{Module::Install::Base};
 	$ISCORE  = 1;
 }
@@ -22,7 +22,6 @@ sub WriteAll {
 	);
 
 	$self->sign(1)                if $args{sign};
-	$self->Meta->write            if $args{meta};
 	$self->admin->WriteAll(%args) if $self->is_admin;
 
 	$self->check_nmake if $args{check_nmake};
@@ -30,11 +29,32 @@ sub WriteAll {
 		$self->makemaker_args( PL_FILES => {} );
 	}
 
+	# Until ExtUtils::MakeMaker support MYMETA.yml, make sure
+	# we clean it up properly ourself.
+	$self->realclean_files('MYMETA.yml');
+
 	if ( $args{inline} ) {
 		$self->Inline->write;
 	} else {
 		$self->Makefile->write;
 	}
+
+	# The Makefile write process adds a couple of dependencies,
+	# so write the META.yml files after the Makefile.
+	if ( $args{meta} ) {
+		$self->Meta->write;
+	}
+
+	# Experimental support for MYMETA
+	if ( $ENV{X_MYMETA} ) {
+		if ( $ENV{X_MYMETA} eq 'JSON' ) {
+			$self->Meta->write_mymeta_json;
+		} else {
+			$self->Meta->write_mymeta_yaml;
+		}
+	}
+
+	return 1;
 }
 
 1;
@@ -17,12 +17,10 @@ package Module::Install;
 #     3. The ./inc/ version of Module::Install loads
 # }
 
-BEGIN {
-	require 5.004;
-}
+use 5.005;
 use strict 'vars';
 
-use vars qw{$VERSION};
+use vars qw{$VERSION $MAIN};
 BEGIN {
 	# All Module::Install core packages now require synchronised versions.
 	# This will be used to ensure we don't accidentally load old or
@@ -30,7 +28,14 @@ BEGIN {
 	# This is not enforced yet, but will be some time in the next few
 	# releases once we can make sure it won't clash with custom
 	# Module::Install extensions.
-	$VERSION = '0.71';
+	$VERSION = '0.91';
+
+	# Storage for the pseudo-singleton
+	$MAIN    = undef;
+
+	*inc::Module::Install::VERSION = *VERSION;
+	@inc::Module::Install::ISA     = __PACKAGE__;
+
 }
 
 
@@ -65,15 +70,26 @@ END_DIE
 # again. This is bad. Rather than taking action to touch it (which
 # is unreliable on some platforms and requires write permissions)
 # for now we should catch this and refuse to run.
-if ( -f $0 and (stat($0))[9] > time ) { die <<"END_DIE" }
+if ( -f $0 ) {
+	my $s = (stat($0))[9];
+
+	# If the modification time is only slightly in the future,
+	# sleep briefly to remove the problem.
+	my $a = $s - time;
+	if ( $a > 0 and $a < 5 ) { sleep 5 }
 
-Your installer $0 has a modification time in the future.
+	# Too far in the future, throw an error.
+	my $t = time;
+	if ( $s > $t ) { die <<"END_DIE" }
+
+Your installer $0 has a modification time in the future ($s > $t).
 
 This is known to create infinite loops in make.
 
 Please correct this, then run $0 again.
 
 END_DIE
+}
 
 
 
@@ -81,7 +97,7 @@ END_DIE
 
 # Build.PL was formerly supported, but no longer is due to excessive
 # difficulty in implementing every single feature twice.
-if ( $0 =~ /Build.PL$/i or -f 'Build.PL' ) { die <<"END_DIE" }
+if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
 
 Module::Install no longer supports Build.PL.
 
@@ -95,14 +111,20 @@ END_DIE
 
 
 
+# To save some more typing in Module::Install installers, every...
+# use inc::Module::Install
+# ...also acts as an implicit use strict.
+$^H |= strict::bits(qw(refs subs vars));
+
+
+
+
+
 use Cwd        ();
 use File::Find ();
 use File::Path ();
 use FindBin;
 
-*inc::Module::Install::VERSION = *VERSION;
-@inc::Module::Install::ISA     = __PACKAGE__;
-
 sub autoload {
 	my $self = shift;
 	my $who  = $self->_caller;
@@ -111,12 +133,22 @@ sub autoload {
 	$sym->{$cwd} = sub {
 		my $pwd = Cwd::cwd();
 		if ( my $code = $sym->{$pwd} ) {
-			# delegate back to parent dirs
+			# Delegate back to parent dirs
 			goto &$code unless $cwd eq $pwd;
 		}
 		$$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+		my $method = $1;
+		if ( uc($method) eq $method ) {
+			# Do nothing
+			return;
+		} elsif ( $method =~ /^_/ and $self->can($method) ) {
+			# Dispatch to the root M:I class
+			return $self->$method(@_);
+		}
+
+		# Dispatch to the appropriate plugin
 		unshift @_, ( $self, $1 );
-		goto &{$self->can('call')} unless uc($1) eq $1;
+		goto &{$self->can('call')};
 	};
 }
 
@@ -141,6 +173,9 @@ sub import {
 	delete $INC{"$self->{file}"};
 	delete $INC{"$self->{path}.pm"};
 
+	# Save to the singleton
+	$MAIN = $self;
+
 	return 1;
 }
 
@@ -154,8 +189,7 @@ sub preload {
 
 	my @exts = @{$self->{extensions}};
 	unless ( @exts ) {
-		my $admin = $self->{admin};
-		@exts = $admin->load_all_extensions;
+		@exts = $self->{admin}->load_all_extensions;
 	}
 
 	my %seen;
@@ -238,7 +272,7 @@ END_DIE
 sub load_extensions {
 	my ($self, $path, $top) = @_;
 
-	unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
+	unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) {
 		unshift @INC, $self->{prefix};
 	}
 
@@ -302,7 +336,7 @@ sub find_extensions {
 
 
 #####################################################################
-# Utility Functions
+# Common Utility Functions
 
 sub _caller {
 	my $depth = 0;
@@ -316,28 +350,81 @@ sub _caller {
 
 sub _read {
 	local *FH;
-	open FH, "< $_[0]" or die "open($_[0]): $!";
-	my $str = do { local $/; <FH> };
+	if ( $] >= 5.006 ) {
+		open( FH, '<', $_[0] ) or die "open($_[0]): $!";
+	} else {
+		open( FH, "< $_[0]"  ) or die "open($_[0]): $!";
+	}
+	my $string = do { local $/; <FH> };
 	close FH or die "close($_[0]): $!";
-	return $str;
+	return $string;
+}
+
+sub _readperl {
+	my $string = Module::Install::_read($_[0]);
+	$string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
+	$string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s;
+	$string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg;
+	return $string;
+}
+
+sub _readpod {
+	my $string = Module::Install::_read($_[0]);
+	$string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
+	return $string if $_[0] =~ /\.pod\z/;
+	$string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg;
+	$string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg;
+	$string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg;
+	$string =~ s/^\n+//s;
+	return $string;
 }
 
 sub _write {
 	local *FH;
-	open FH, "> $_[0]" or die "open($_[0]): $!";
-	foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!" }
+	if ( $] >= 5.006 ) {
+		open( FH, '>', $_[0] ) or die "open($_[0]): $!";
+	} else {
+		open( FH, "> $_[0]"  ) or die "open($_[0]): $!";
+	}
+	foreach ( 1 .. $#_ ) {
+		print FH $_[$_] or die "print($_[0]): $!";
+	}
 	close FH or die "close($_[0]): $!";
 }
 
-sub _version {
+# _version is for processing module versions (eg, 1.03_05) not
+# Perl versions (eg, 5.8.1).
+sub _version ($) {
 	my $s = shift || 0;
-	   $s =~ s/^(\d+)\.?//;
+	my $d =()= $s =~ /(\.)/g;
+	if ( $d >= 2 ) {
+		# Normalise multipart versions
+		$s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg;
+	}
+	$s =~ s/^(\d+)\.?//;
 	my $l = $1 || 0;
-	my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g;
-	   $l = $l . '.' . join '', @v if @v;
+	my @v = map {
+		$_ . '0' x (3 - length $_)
+	} $s =~ /(\d{1,3})\D?/g;
+	$l = $l . '.' . join '', @v if @v;
 	return $l + 0;
 }
 
+sub _cmp ($$) {
+	_version($_[0]) <=> _version($_[1]);
+}
+
+# Cloned from Params::Util::_CLASS
+sub _CLASS ($) {
+	(
+		defined $_[0]
+		and
+		! ref $_[0]
+		and
+		$_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s
+	) ? $_[0] : undef;
+}
+
 1;
 
-# Copyright 2008 Adam Kennedy.
+# Copyright 2008 - 2009 Adam Kennedy.
@@ -5,7 +5,7 @@ package Test::Base;
 use 5.006001;
 use Spiffy 0.30 -Base;
 use Spiffy ':XXX';
-our $VERSION = '0.54';
+our $VERSION = '0.59';
 
 my @test_more_exports;
 BEGIN {
@@ -29,6 +29,7 @@ our @EXPORT = (@test_more_exports, qw(
     delimiters spec_file spec_string 
     filters filters_delay filter_arguments
     run run_compare run_is run_is_deeply run_like run_unlike 
+    skip_all_unless_require is_deep run_is_deep
     WWW XXX YYY ZZZ
     tie_output no_diag_on_only
 
@@ -370,6 +371,34 @@ sub run_unlike() {
     }
 }
 
+sub skip_all_unless_require() {
+    (my ($self), @_) = find_my_self(@_);
+    my $module = shift;
+    eval "require $module; 1"
+        or Test::More::plan(
+            skip_all => "$module failed to load"
+        );
+}
+
+sub is_deep() {
+    (my ($self), @_) = find_my_self(@_);
+    require Test::Deep;
+    Test::Deep::cmp_deeply(@_);
+}
+
+sub run_is_deep() {
+    (my ($self), @_) = find_my_self(@_);
+    $self->_assert_plan;
+    my ($x, $y) = $self->_section_names(@_);
+    for my $block (@{$self->block_list}) {
+        next unless exists($block->{$x}) and exists($block->{$y});
+        $block->run_filters unless $block->is_filtered;
+        is_deep($block->$x, $block->$y, 
+           $block->name ? $block->name : ()
+          );
+    }
+}
+
 sub _pre_eval {
     my $spec = shift;
     return $spec unless $spec =~
@@ -585,11 +614,13 @@ sub run_filters {
             my $function = "main::$filter";
             no strict 'refs';
             if (defined &$function) {
-                local $_ = join '', @value;
+                local $_ =
+                    (@value == 1 and not defined($value[0])) ? undef :
+                        join '', @value;
                 my $old = $_;
                 @value = &$function(@value);
                 if (not(@value) or 
-                    @value == 1 and $value[0] =~ /\A(\d+|)\z/
+                    @value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/
                 ) {
                     if ($value[0] && $_ eq $old) {
                         Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't.");
@@ -648,4 +679,6 @@ sub _get_filters {
 
 __DATA__
 
-#line 1328
+=encoding utf8
+
+#line 1376
@@ -1,57 +1,51 @@
 #line 1
 package Test::Builder::Module;
 
+use strict;
+
 use Test::Builder;
 
 require Exporter;
-@ISA = qw(Exporter);
-
-$VERSION = '0.72';
-
-use strict;
+our @ISA = qw(Exporter);
 
-# 5.004's Exporter doesn't have export_to_level.
-my $_export_to_level = sub {
-      my $pkg = shift;
-      my $level = shift;
-      (undef) = shift;                  # redundant arg
-      my $callpkg = caller($level);
-      $pkg->export($callpkg, @_);
-};
+our $VERSION = '0.94';
+$VERSION = eval $VERSION;      ## no critic (BuiltinFunctions::ProhibitStringyEval)
 
 
-#line 82
+#line 74
 
 sub import {
     my($class) = shift;
 
+    # Don't run all this when loading ourself.
+    return 1 if $class eq 'Test::Builder::Module';
+
     my $test = $class->builder;
 
     my $caller = caller;
 
     $test->exported_to($caller);
 
-    $class->import_extra(\@_);
-    my(@imports) = $class->_strip_imports(\@_);
+    $class->import_extra( \@_ );
+    my(@imports) = $class->_strip_imports( \@_ );
 
     $test->plan(@_);
 
-    $class->$_export_to_level(1, $class, @imports);
+    $class->export_to_level( 1, $class, @imports );
 }
 
-
 sub _strip_imports {
     my $class = shift;
     my $list  = shift;
 
     my @imports = ();
     my @other   = ();
-    my $idx = 0;
+    my $idx     = 0;
     while( $idx <= $#{$list} ) {
         my $item = $list->[$idx];
 
         if( defined $item and $item eq 'import' ) {
-            push @imports, @{$list->[$idx+1]};
+            push @imports, @{ $list->[ $idx + 1 ] };
             $idx++;
         }
         else {
@@ -66,17 +60,14 @@ sub _strip_imports {
     return @imports;
 }
 
+#line 137
 
-#line 144
-
-sub import_extra {}
+sub import_extra { }
 
-
-#line 175
+#line 167
 
 sub builder {
     return Test::Builder->new;
 }
 
-
 1;
@@ -1,26 +1,29 @@
 #line 1
 package Test::Builder;
 
-use 5.004;
+use 5.006;
+use strict;
+use warnings;
 
-# $^C was only introduced in 5.005-ish.  We do this to prevent
-# use of uninitialized value warnings in older perls.
-$^C ||= 0;
+our $VERSION = '0.94';
+$VERSION = eval $VERSION;    ## no critic (BuiltinFunctions::ProhibitStringyEval)
+
+BEGIN {
+    if( $] < 5.008 ) {
+        require Test::Builder::IO::Scalar;
+    }
+}
 
-use strict;
-use vars qw($VERSION);
-$VERSION = '0.72';
-$VERSION = eval $VERSION;    # make the alpha version come out as a number
 
 # Make Test::Builder thread-safe for ithreads.
 BEGIN {
     use Config;
     # Load threads::shared when threads are turned on.
     # 5.8.0's threads are so busted we no longer support them.
-    if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'}) {
+    if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'} ) {
         require threads::shared;
 
-        # Hack around YET ANOTHER threads::shared bug.  It would 
+        # Hack around YET ANOTHER threads::shared bug.  It would
         # occassionally forget the contents of the variable when sharing it.
         # So we first copy the data, then share, then put our copy back.
         *share = sub (\[$@%]) {
@@ -28,31 +31,31 @@ BEGIN {
             my $data;
 
             if( $type eq 'HASH' ) {
-                %$data = %{$_[0]};
+                %$data = %{ $_[0] };
             }
             elsif( $type eq 'ARRAY' ) {
-                @$data = @{$_[0]};
+                @$data = @{ $_[0] };
             }
             elsif( $type eq 'SCALAR' ) {
-                $$data = ${$_[0]};
+                $$data = ${ $_[0] };
             }
             else {
-                die("Unknown type: ".$type);
+                die( "Unknown type: " . $type );
             }
 
-            $_[0] = &threads::shared::share($_[0]);
+            $_[0] = &threads::shared::share( $_[0] );
 
             if( $type eq 'HASH' ) {
-                %{$_[0]} = %$data;
+                %{ $_[0] } = %$data;
             }
             elsif( $type eq 'ARRAY' ) {
-                @{$_[0]} = @$data;
+                @{ $_[0] } = @$data;
             }
             elsif( $type eq 'SCALAR' ) {
-                ${$_[0]} = $$data;
+                ${ $_[0] } = $$data;
             }
             else {
-                die("Unknown type: ".$type);
+                die( "Unknown type: " . $type );
             }
 
             return $_[0];
@@ -66,18 +69,17 @@ BEGIN {
     }
 }
 
+#line 117
 
-#line 128
+our $Test = Test::Builder->new;
 
-my $Test = Test::Builder->new;
 sub new {
     my($class) = shift;
     $Test ||= $class->create;
     return $Test;
 }
 
-
-#line 150
+#line 139
 
 sub create {
     my $class = shift;
@@ -88,178 +90,387 @@ sub create {
     return $self;
 }
 
-#line 169
+#line 168
+
+sub child {
+    my( $self, $name ) = @_;
+
+    if( $self->{Child_Name} ) {
+        $self->croak("You already have a child named ($self->{Child_Name}) running");
+    }
+
+    my $child = bless {}, ref $self;
+    $child->reset;
+
+    # Add to our indentation
+    $child->_indent( $self->_indent . '    ' );
+    $child->{$_} = $self->{$_} foreach qw{Out_FH Todo_FH Fail_FH};
+
+    # This will be reset in finalize. We do this here lest one child failure
+    # cause all children to fail.
+    $child->{Child_Error} = $?;
+    $?                    = 0;
+    $child->{Parent}      = $self;
+    $child->{Name}        = $name || "Child of " . $self->name;
+    $self->{Child_Name}   = $child->name;
+    return $child;
+}
+
+
+#line 201
+
+sub subtest {
+    my $self = shift;
+    my($name, $subtests) = @_;
+
+    if ('CODE' ne ref $subtests) {
+        $self->croak("subtest()'s second argument must be a code ref");
+    }
+
+    # Turn the child into the parent so anyone who has stored a copy of
+    # the Test::Builder singleton will get the child.
+    my $child = $self->child($name);
+    my %parent = %$self;
+    %$self = %$child;
+
+    my $error;
+    if( !eval { $subtests->(); 1 } ) {
+        $error = $@;
+    }
+
+    # Restore the parent and the copied child.
+    %$child = %$self;
+    %$self = %parent;
+
+    # Die *after* we restore the parent.
+    die $error if $error and !eval { $error->isa('Test::Builder::Exception') };
+
+    return $child->finalize;
+}
+
+
+#line 250
+
+sub finalize {
+    my $self = shift;
+
+    return unless $self->parent;
+    if( $self->{Child_Name} ) {
+        $self->croak("Can't call finalize() with child ($self->{Child_Name}) active");
+    }
+    $self->_ending;
+
+    # XXX This will only be necessary for TAP envelopes (we think)
+    #$self->_print( $self->is_passing ? "PASS\n" : "FAIL\n" );
+
+    my $ok = 1;
+    $self->parent->{Child_Name} = undef;
+    if ( $self->{Skip_All} ) {
+        $self->parent->skip($self->{Skip_All});
+    }
+    elsif ( not @{ $self->{Test_Results} } ) {
+        $self->parent->ok( 0, sprintf q[No tests run for subtest "%s"], $self->name );
+    }
+    else {
+        $self->parent->ok( $self->is_passing, $self->name );
+    }
+    $? = $self->{Child_Error};
+    delete $self->{Parent};
+
+    return $self->is_passing;
+}
+
+sub _indent      {
+    my $self = shift;
 
-use vars qw($Level);
+    if( @_ ) {
+        $self->{Indent} = shift;
+    }
 
-sub reset {
-    my ($self) = @_;
+    return $self->{Indent};
+}
+
+#line 300
+
+sub parent { shift->{Parent} }
+
+#line 312
+
+sub name { shift->{Name} }
+
+sub DESTROY {
+    my $self = shift;
+    if ( $self->parent ) {
+        my $name = $self->name;
+        $self->diag(<<"FAIL");
+Child ($name) exited without calling finalize()
+FAIL
+        $self->parent->{In_Destroy} = 1;
+        $self->parent->ok(0, $name);
+    }
+}
+
+#line 336
+
+our $Level;
+
+sub reset {    ## no critic (Subroutines::ProhibitBuiltinHomonyms)
+    my($self) = @_;
 
     # We leave this a global because it has to be localized and localizing
     # hash keys is just asking for pain.  Also, it was documented.
     $Level = 1;
 
-    $self->{Test_Died}    = 0;
+    $self->{Name}         = $0;
+    $self->is_passing(1);
+    $self->{Ending}       = 0;
     $self->{Have_Plan}    = 0;
     $self->{No_Plan}      = 0;
+    $self->{Have_Output_Plan} = 0;
+
     $self->{Original_Pid} = $$;
+    $self->{Child_Name}   = undef;
+    $self->{Indent}     ||= '';
 
-    share($self->{Curr_Test});
-    $self->{Curr_Test}    = 0;
-    $self->{Test_Results} = &share([]);
+    share( $self->{Curr_Test} );
+    $self->{Curr_Test} = 0;
+    $self->{Test_Results} = &share( [] );
 
     $self->{Exported_To}    = undef;
     $self->{Expected_Tests} = 0;
 
-    $self->{Skip_All}   = 0;
-
-    $self->{Use_Nums}   = 1;
+    $self->{Skip_All} = 0;
 
-    $self->{No_Header}  = 0;
-    $self->{No_Ending}  = 0;
+    $self->{Use_Nums} = 1;
 
-    $self->_dup_stdhandles unless $^C;
+    $self->{No_Header} = 0;
+    $self->{No_Ending} = 0;
 
-    return undef;
-}
-
-#line 221
+    $self->{Todo}       = undef;
+    $self->{Todo_Stack} = [];
+    $self->{Start_Todo} = 0;
+    $self->{Opened_Testhandles} = 0;
 
-sub exported_to {
-    my($self, $pack) = @_;
+    $self->_dup_stdhandles;
 
-    if( defined $pack ) {
-        $self->{Exported_To} = $pack;
-    }
-    return $self->{Exported_To};
+    return;
 }
 
-#line 243
+#line 414
+
+my %plan_cmds = (
+    no_plan     => \&no_plan,
+    skip_all    => \&skip_all,
+    tests       => \&_plan_tests,
+);
 
 sub plan {
-    my($self, $cmd, $arg) = @_;
+    my( $self, $cmd, $arg ) = @_;
 
     return unless $cmd;
 
     local $Level = $Level + 1;
 
-    if( $self->{Have_Plan} ) {
-        $self->croak("You tried to plan twice");
-    }
+    $self->croak("You tried to plan twice") if $self->{Have_Plan};
 
-    if( $cmd eq 'no_plan' ) {
-        $self->no_plan;
-    }
-    elsif( $cmd eq 'skip_all' ) {
-        return $self->skip_all($arg);
-    }
-    elsif( $cmd eq 'tests' ) {
-        if( $arg ) {
-            local $Level = $Level + 1;
-            return $self->expected_tests($arg);
-        }
-        elsif( !defined $arg ) {
-            $self->croak("Got an undefined number of tests");
-        }
-        elsif( !$arg ) {
-            $self->croak("You said to run 0 tests");
-        }
+    if( my $method = $plan_cmds{$cmd} ) {
+        local $Level = $Level + 1;
+        $self->$method($arg);
     }
     else {
-        my @args = grep { defined } ($cmd, $arg);
+        my @args = grep { defined } ( $cmd, $arg );
         $self->croak("plan() doesn't understand @args");
     }
 
     return 1;
 }
 
-#line 290
+
+sub _plan_tests {
+    my($self, $arg) = @_;
+
+    if($arg) {
+        local $Level = $Level + 1;
+        return $self->expected_tests($arg);
+    }
+    elsif( !defined $arg ) {
+        $self->croak("Got an undefined number of tests");
+    }
+    else {
+        $self->croak("You said to run 0 tests");
+    }
+
+    return;
+}
+
+
+#line 470
 
 sub expected_tests {
     my $self = shift;
     my($max) = @_;
 
-    if( @_ ) {
+    if(@_) {
         $self->croak("Number of tests must be a positive integer.  You gave it '$max'")
-          unless $max =~ /^\+?\d+$/ and $max > 0;
+          unless $max =~ /^\+?\d+$/;
 
         $self->{Expected_Tests} = $max;
         $self->{Have_Plan}      = 1;
 
-        $self->_print("1..$max\n") unless $self->no_header;
+        $self->_output_plan($max) unless $self->no_header;
     }
     return $self->{Expected_Tests};
 }
 
-
-#line 315
+#line 494
 
 sub no_plan {
-    my $self = shift;
+    my($self, $arg) = @_;
+
+    $self->carp("no_plan takes no arguments") if $arg;
 
     $self->{No_Plan}   = 1;
     $self->{Have_Plan} = 1;
+
+    return 1;
+}
+
+
+#line 528
+
+sub _output_plan {
+    my($self, $max, $directive, $reason) = @_;
+
+    $self->carp("The plan was already output") if $self->{Have_Output_Plan};
+
+    my $plan = "1..$max";
+    $plan .= " # $directive" if defined $directive;
+    $plan .= " $reason"      if defined $reason;
+
+    $self->_print("$plan\n");
+
+    $self->{Have_Output_Plan} = 1;
+
+    return;
+}
+
+#line 579
+
+sub done_testing {
+    my($self, $num_tests) = @_;
+
+    # If done_testing() specified the number of tests, shut off no_plan.
+    if( defined $num_tests ) {
+        $self->{No_Plan} = 0;
+    }
+    else {
+        $num_tests = $self->current_test;
+    }
+
+    if( $self->{Done_Testing} ) {
+        my($file, $line) = @{$self->{Done_Testing}}[1,2];
+        $self->ok(0, "done_testing() was already called at $file line $line");
+        return;
+    }
+
+    $self->{Done_Testing} = [caller];
+
+    if( $self->expected_tests && $num_tests != $self->expected_tests ) {
+        $self->ok(0, "planned to run @{[ $self->expected_tests ]} ".
+                     "but done_testing() expects $num_tests");
+    }
+    else {
+        $self->{Expected_Tests} = $num_tests;
+    }
+
+    $self->_output_plan($num_tests) unless $self->{Have_Output_Plan};
+
+    $self->{Have_Plan} = 1;
+
+    # The wrong number of tests were run
+    $self->is_passing(0) if $self->{Expected_Tests} != $self->{Curr_Test};
+
+    # No tests were run
+    $self->is_passing(0) if $self->{Curr_Test} == 0;
+
+    return 1;
 }
 
-#line 330
+
+#line 630
 
 sub has_plan {
     my $self = shift;
 
-    return($self->{Expected_Tests}) if $self->{Expected_Tests};
+    return( $self->{Expected_Tests} ) if $self->{Expected_Tests};
     return('no_plan') if $self->{No_Plan};
     return(undef);
-};
-
+}
 
-#line 348
+#line 647
 
 sub skip_all {
-    my($self, $reason) = @_;
+    my( $self, $reason ) = @_;
 
-    my $out = "1..0";
-    $out .= " # Skip $reason" if $reason;
-    $out .= "\n";
-
-    $self->{Skip_All} = 1;
+    $self->{Skip_All} = $self->parent ? $reason : 1;
 
-    $self->_print($out) unless $self->no_header;
+    $self->_output_plan(0, "SKIP", $reason) unless $self->no_header;
+    if ( $self->parent ) {
+        die bless {} => 'Test::Builder::Exception';
+    }
     exit(0);
 }
 
-#line 382
+#line 672
+
+sub exported_to {
+    my( $self, $pack ) = @_;
+
+    if( defined $pack ) {
+        $self->{Exported_To} = $pack;
+    }
+    return $self->{Exported_To};
+}
+
+#line 702
 
 sub ok {
-    my($self, $test, $name) = @_;
+    my( $self, $test, $name ) = @_;
 
+    if ( $self->{Child_Name} and not $self->{In_Destroy} ) {
+        $name = 'unnamed test' unless defined $name;
+        $self->is_passing(0);
+        $self->croak("Cannot run test ($name) with active children");
+    }
     # $test might contain an object which we don't want to accidentally
     # store, so we turn it into a boolean.
     $test = $test ? 1 : 0;
 
-    $self->_plan_check;
-
     lock $self->{Curr_Test};
     $self->{Curr_Test}++;
 
     # In case $name is a string overloaded object, force it to stringify.
-    $self->_unoverload_str(\$name);
+    $self->_unoverload_str( \$name );
 
-    $self->diag(<<ERR) if defined $name and $name =~ /^[\d\s]+$/;
+    $self->diag(<<"ERR") if defined $name and $name =~ /^[\d\s]+$/;
     You named your test '$name'.  You shouldn't use numbers for your test names.
     Very confusing.
 ERR
 
-    my($pack, $file, $line) = $self->caller;
+    # Capture the value of $TODO for the rest of this ok() call
+    # so it can more easily be found by other routines.
+    my $todo    = $self->todo();
+    my $in_todo = $self->in_todo;
+    local $self->{Todo} = $todo if $in_todo;
 
-    my $todo = $self->todo($pack);
-    $self->_unoverload_str(\$todo);
+    $self->_unoverload_str( \$todo );
 
     my $out;
-    my $result = &share({});
+    my $result = &share( {} );
 
-    unless( $test ) {
+    unless($test) {
         $out .= "not ";
-        @$result{ 'ok', 'actual_ok' } = ( ( $todo ? 1 : 0 ), 0 );
+        @$result{ 'ok', 'actual_ok' } = ( ( $self->in_todo ? 1 : 0 ), 0 );
     }
     else {
         @$result{ 'ok', 'actual_ok' } = ( 1, $test );
@@ -269,16 +480,16 @@ ERR
     $out .= " $self->{Curr_Test}" if $self->use_numbers;
 
     if( defined $name ) {
-        $name =~ s|#|\\#|g;     # # in a name can confuse Test::Harness.
-        $out   .= " - $name";
+        $name =~ s|#|\\#|g;    # # in a name can confuse Test::Harness.
+        $out .= " - $name";
         $result->{name} = $name;
     }
     else {
         $result->{name} = '';
     }
 
-    if( $todo ) {
-        $out   .= " # TODO $todo";
+    if( $self->in_todo ) {
+        $out .= " # TODO $todo";
         $result->{reason} = $todo;
         $result->{type}   = 'todo';
     }
@@ -287,256 +498,306 @@ ERR
         $result->{type}   = '';
     }
 
-    $self->{Test_Results}[$self->{Curr_Test}-1] = $result;
+    $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = $result;
     $out .= "\n";
 
     $self->_print($out);
 
-    unless( $test ) {
-        my $msg = $todo ? "Failed (TODO)" : "Failed";
-        $self->_print_diag("\n") if $ENV{HARNESS_ACTIVE};
+    unless($test) {
+        my $msg = $self->in_todo ? "Failed (TODO)" : "Failed";
+        $self->_print_to_fh( $self->_diag_fh, "\n" ) if $ENV{HARNESS_ACTIVE};
+
+        my( undef, $file, $line ) = $self->caller;
+        if( defined $name ) {
+            $self->diag(qq[  $msg test '$name'\n]);
+            $self->diag(qq[  at $file line $line.\n]);
+        }
+        else {
+            $self->diag(qq[  $msg test at $file line $line.\n]);
+        }
+    }
+
+    $self->is_passing(0) unless $test || $self->in_todo;
 
-	if( defined $name ) {
-	    $self->diag(qq[  $msg test '$name'\n]);
-	    $self->diag(qq[  at $file line $line.\n]);
-	}
-	else {
-	    $self->diag(qq[  $msg test at $file line $line.\n]);
-	}
-    } 
+    # Check that we haven't violated the plan
+    $self->_check_is_passing_plan();
 
     return $test ? 1 : 0;
 }
 
 
+# Check that we haven't yet violated the plan and set
+# is_passing() accordingly
+sub _check_is_passing_plan {
+    my $self = shift;
+
+    my $plan = $self->has_plan;
+    return unless defined $plan;        # no plan yet defined
+    return unless $plan !~ /\D/;        # no numeric plan
+    $self->is_passing(0) if $plan < $self->{Curr_Test};
+}
+
+
 sub _unoverload {
-    my $self  = shift;
-    my $type  = shift;
+    my $self = shift;
+    my $type = shift;
 
-    $self->_try(sub { require overload } ) || return;
+    $self->_try(sub { require overload; }, die_on_fail => 1);
 
     foreach my $thing (@_) {
         if( $self->_is_object($$thing) ) {
-            if( my $string_meth = overload::Method($$thing, $type) ) {
+            if( my $string_meth = overload::Method( $$thing, $type ) ) {
                 $$thing = $$thing->$string_meth();
             }
         }
     }
-}
 
+    return;
+}
 
 sub _is_object {
-    my($self, $thing) = @_;
+    my( $self, $thing ) = @_;
 
-    return $self->_try(sub { ref $thing && $thing->isa('UNIVERSAL') }) ? 1 : 0;
+    return $self->_try( sub { ref $thing && $thing->isa('UNIVERSAL') } ) ? 1 : 0;
 }
 
-
 sub _unoverload_str {
     my $self = shift;
 
-    $self->_unoverload(q[""], @_);
-}    
+    return $self->_unoverload( q[""], @_ );
+}
 
 sub _unoverload_num {
     my $self = shift;
 
-    $self->_unoverload('0+', @_);
+    $self->_unoverload( '0+', @_ );
 
     for my $val (@_) {
         next unless $self->_is_dualvar($$val);
-        $$val = $$val+0;
+        $$val = $$val + 0;
     }
-}
 
+    return;
+}
 
 # This is a hack to detect a dualvar such as $!
 sub _is_dualvar {
-    my($self, $val) = @_;
-
-    local $^W = 0;
-    my $numval = $val+0;
-    return 1 if $numval != 0 and $numval ne $val;
-}
+    my( $self, $val ) = @_;
 
+    # Objects are not dualvars.
+    return 0 if ref $val;
 
+    no warnings 'numeric';
+    my $numval = $val + 0;
+    return $numval != 0 and $numval ne $val ? 1 : 0;
+}
 
-#line 530
+#line 876
 
 sub is_eq {
-    my($self, $got, $expect, $name) = @_;
+    my( $self, $got, $expect, $name ) = @_;
     local $Level = $Level + 1;
 
-    $self->_unoverload_str(\$got, \$expect);
+    $self->_unoverload_str( \$got, \$expect );
 
     if( !defined $got || !defined $expect ) {
         # undef only matches undef and nothing else
         my $test = !defined $got && !defined $expect;
 
-        $self->ok($test, $name);
-        $self->_is_diag($got, 'eq', $expect) unless $test;
+        $self->ok( $test, $name );
+        $self->_is_diag( $got, 'eq', $expect ) unless $test;
         return $test;
     }
 
-    return $self->cmp_ok($got, 'eq', $expect, $name);
+    return $self->cmp_ok( $got, 'eq', $expect, $name );
 }
 
 sub is_num {
-    my($self, $got, $expect, $name) = @_;
+    my( $self, $got, $expect, $name ) = @_;
     local $Level = $Level + 1;
 
-    $self->_unoverload_num(\$got, \$expect);
+    $self->_unoverload_num( \$got, \$expect );
 
     if( !defined $got || !defined $expect ) {
         # undef only matches undef and nothing else
         my $test = !defined $got && !defined $expect;
 
-        $self->ok($test, $name);
-        $self->_is_diag($got, '==', $expect) unless $test;
+        $self->ok( $test, $name );
+        $self->_is_diag( $got, '==', $expect ) unless $test;
         return $test;
     }
 
-    return $self->cmp_ok($got, '==', $expect, $name);
+    return $self->cmp_ok( $got, '==', $expect, $name );
 }
 
-sub _is_diag {
-    my($self, $got, $type, $expect) = @_;
+sub _diag_fmt {
+    my( $self, $type, $val ) = @_;
 
-    foreach my $val (\$got, \$expect) {
-        if( defined $$val ) {
-            if( $type eq 'eq' ) {
-                # quote and force string context
-                $$val = "'$$val'"
-            }
-            else {
-                # force numeric context
-                $self->_unoverload_num($val);
-            }
+    if( defined $$val ) {
+        if( $type eq 'eq' or $type eq 'ne' ) {
+            # quote and force string context
+            $$val = "'$$val'";
         }
         else {
-            $$val = 'undef';
+            # force numeric context
+            $self->_unoverload_num($val);
         }
     }
+    else {
+        $$val = 'undef';
+    }
 
-    return $self->diag(sprintf <<DIAGNOSTIC, $got, $expect);
-         got: %s
-    expected: %s
+    return;
+}
+
+sub _is_diag {
+    my( $self, $got, $type, $expect ) = @_;
+
+    $self->_diag_fmt( $type, $_ ) for \$got, \$expect;
+
+    local $Level = $Level + 1;
+    return $self->diag(<<"DIAGNOSTIC");
+         got: $got
+    expected: $expect
 DIAGNOSTIC
 
-}    
+}
+
+sub _isnt_diag {
+    my( $self, $got, $type ) = @_;
+
+    $self->_diag_fmt( $type, \$got );
+
+    local $Level = $Level + 1;
+    return $self->diag(<<"DIAGNOSTIC");
+         got: $got
+    expected: anything else
+DIAGNOSTIC
+}
 
-#line 608
+#line 973
 
 sub isnt_eq {
-    my($self, $got, $dont_expect, $name) = @_;
+    my( $self, $got, $dont_expect, $name ) = @_;
     local $Level = $Level + 1;
 
     if( !defined $got || !defined $dont_expect ) {
         # undef only matches undef and nothing else
         my $test = defined $got || defined $dont_expect;
 
-        $self->ok($test, $name);
-        $self->_cmp_diag($got, 'ne', $dont_expect) unless $test;
+        $self->ok( $test, $name );
+        $self->_isnt_diag( $got, 'ne' ) unless $test;
         return $test;
     }
 
-    return $self->cmp_ok($got, 'ne', $dont_expect, $name);
+    return $self->cmp_ok( $got, 'ne', $dont_expect, $name );
 }
 
 sub isnt_num {
-    my($self, $got, $dont_expect, $name) = @_;
+    my( $self, $got, $dont_expect, $name ) = @_;
     local $Level = $Level + 1;
 
     if( !defined $got || !defined $dont_expect ) {
         # undef only matches undef and nothing else
         my $test = defined $got || defined $dont_expect;
 
-        $self->ok($test, $name);
-        $self->_cmp_diag($got, '!=', $dont_expect) unless $test;
+        $self->ok( $test, $name );
+        $self->_isnt_diag( $got, '!=' ) unless $test;
         return $test;
     }
 
-    return $self->cmp_ok($got, '!=', $dont_expect, $name);
+    return $self->cmp_ok( $got, '!=', $dont_expect, $name );
 }
 
-
-#line 660
+#line 1022
 
 sub like {
-    my($self, $this, $regex, $name) = @_;
+    my( $self, $this, $regex, $name ) = @_;
 
     local $Level = $Level + 1;
-    $self->_regex_ok($this, $regex, '=~', $name);
+    return $self->_regex_ok( $this, $regex, '=~', $name );
 }
 
 sub unlike {
-    my($self, $this, $regex, $name) = @_;
+    my( $self, $this, $regex, $name ) = @_;
 
     local $Level = $Level + 1;
-    $self->_regex_ok($this, $regex, '!~', $name);
+    return $self->_regex_ok( $this, $regex, '!~', $name );
 }
 
+#line 1046
 
-#line 685
-
-
-my %numeric_cmps = map { ($_, 1) } 
-                       ("<",  "<=", ">",  ">=", "==", "!=", "<=>");
+my %numeric_cmps = map { ( $_, 1 ) } ( "<", "<=", ">", ">=", "==", "!=", "<=>" );
 
 sub cmp_ok {
-    my($self, $got, $type, $expect, $name) = @_;
-
-    # Treat overloaded objects as numbers if we're asked to do a
-    # numeric comparison.
-    my $unoverload = $numeric_cmps{$type} ? '_unoverload_num'
-                                          : '_unoverload_str';
-
-    $self->$unoverload(\$got, \$expect);
-
+    my( $self, $got, $type, $expect, $name ) = @_;
 
     my $test;
+    my $error;
     {
-        local($@,$!,$SIG{__DIE__});  # isolate eval
+        ## no critic (BuiltinFunctions::ProhibitStringyEval)
 
-        my $code = $self->_caller_context;
+        local( $@, $!, $SIG{__DIE__} );    # isolate eval
 
-        # Yes, it has to look like this or 5.4.5 won't see the #line directive.
-        # Don't ask me, man, I just work here.
-        $test = eval "
-$code" . "\$got $type \$expect;";
+        my($pack, $file, $line) = $self->caller();
 
+        $test = eval qq[
+#line 1 "cmp_ok [from $file line $line]"
+\$got $type \$expect;
+];
+        $error = $@;
     }
     local $Level = $Level + 1;
-    my $ok = $self->ok($test, $name);
+    my $ok = $self->ok( $test, $name );
+
+    # Treat overloaded objects as numbers if we're asked to do a
+    # numeric comparison.
+    my $unoverload
+      = $numeric_cmps{$type}
+      ? '_unoverload_num'
+      : '_unoverload_str';
+
+    $self->diag(<<"END") if $error;
+An error occurred while using $type:
+------------------------------------
+$error
+------------------------------------
+END
+
+    unless($ok) {
+        $self->$unoverload( \$got, \$expect );
 
-    unless( $ok ) {
         if( $type =~ /^(eq|==)$/ ) {
-            $self->_is_diag($got, $type, $expect);
+            $self->_is_diag( $got, $type, $expect );
+        }
+        elsif( $type =~ /^(ne|!=)$/ ) {
+            $self->_isnt_diag( $got, $type );
         }
         else {
-            $self->_cmp_diag($got, $type, $expect);
+            $self->_cmp_diag( $got, $type, $expect );
         }
     }
     return $ok;
 }
 
 sub _cmp_diag {
-    my($self, $got, $type, $expect) = @_;
-    
+    my( $self, $got, $type, $expect ) = @_;
+
     $got    = defined $got    ? "'$got'"    : 'undef';
     $expect = defined $expect ? "'$expect'" : 'undef';
-    return $self->diag(sprintf <<DIAGNOSTIC, $got, $type, $expect);
-    %s
-        %s
-    %s
+
+    local $Level = $Level + 1;
+    return $self->diag(<<"DIAGNOSTIC");
+    $got
+        $type
+    $expect
 DIAGNOSTIC
 }
 
-
 sub _caller_context {
     my $self = shift;
 
-    my($pack, $file, $line) = $self->caller(1);
+    my( $pack, $file, $line ) = $self->caller(1);
 
     my $code = '';
     $code .= "#line $line $file\n" if defined $file and defined $line;
@@ -544,129 +805,137 @@ sub _caller_context {
     return $code;
 }
 
-#line 771
+#line 1145
 
 sub BAIL_OUT {
-    my($self, $reason) = @_;
+    my( $self, $reason ) = @_;
 
     $self->{Bailed_Out} = 1;
     $self->_print("Bail out!  $reason");
     exit 255;
 }
 
-#line 784
-
-*BAILOUT = \&BAIL_OUT;
+#line 1158
 
+{
+    no warnings 'once';
+    *BAILOUT = \&BAIL_OUT;
+}
 
-#line 796
+#line 1172
 
 sub skip {
-    my($self, $why) = @_;
+    my( $self, $why ) = @_;
     $why ||= '';
-    $self->_unoverload_str(\$why);
-
-    $self->_plan_check;
+    $self->_unoverload_str( \$why );
 
-    lock($self->{Curr_Test});
+    lock( $self->{Curr_Test} );
     $self->{Curr_Test}++;
 
-    $self->{Test_Results}[$self->{Curr_Test}-1] = &share({
-        'ok'      => 1,
-        actual_ok => 1,
-        name      => '',
-        type      => 'skip',
-        reason    => $why,
-    });
+    $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share(
+        {
+            'ok'      => 1,
+            actual_ok => 1,
+            name      => '',
+            type      => 'skip',
+            reason    => $why,
+        }
+    );
 
     my $out = "ok";
-    $out   .= " $self->{Curr_Test}" if $self->use_numbers;
-    $out   .= " # skip";
-    $out   .= " $why"       if length $why;
-    $out   .= "\n";
+    $out .= " $self->{Curr_Test}" if $self->use_numbers;
+    $out .= " # skip";
+    $out .= " $why"               if length $why;
+    $out .= "\n";
 
     $self->_print($out);
 
     return 1;
 }
 
-
-#line 838
+#line 1213
 
 sub todo_skip {
-    my($self, $why) = @_;
+    my( $self, $why ) = @_;
     $why ||= '';
 
-    $self->_plan_check;
-
-    lock($self->{Curr_Test});
+    lock( $self->{Curr_Test} );
     $self->{Curr_Test}++;
 
-    $self->{Test_Results}[$self->{Curr_Test}-1] = &share({
-        'ok'      => 1,
-        actual_ok => 0,
-        name      => '',
-        type      => 'todo_skip',
-        reason    => $why,
-    });
+    $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share(
+        {
+            'ok'      => 1,
+            actual_ok => 0,
+            name      => '',
+            type      => 'todo_skip',
+            reason    => $why,
+        }
+    );
 
     my $out = "not ok";
-    $out   .= " $self->{Curr_Test}" if $self->use_numbers;
-    $out   .= " # TODO & SKIP $why\n";
+    $out .= " $self->{Curr_Test}" if $self->use_numbers;
+    $out .= " # TODO & SKIP $why\n";
 
     $self->_print($out);
 
     return 1;
 }
 
-
-#line 916
-
+#line 1293
 
 sub maybe_regex {
-    my ($self, $regex) = @_;
+    my( $self, $regex ) = @_;
     my $usable_regex = undef;
 
     return $usable_regex unless defined $regex;
 
-    my($re, $opts);
+    my( $re, $opts );
 
     # Check for qr/foo/
-    if( ref $regex eq 'Regexp' ) {
+    if( _is_qr($regex) ) {
         $usable_regex = $regex;
     }
     # Check for '/foo/' or 'm,foo,'
-    elsif( ($re, $opts)        = $regex =~ m{^ /(.*)/ (\w*) $ }sx           or
-           (undef, $re, $opts) = $regex =~ m,^ m([^\w\s]) (.+) \1 (\w*) $,sx
-         )
+    elsif(( $re, $opts )        = $regex =~ m{^ /(.*)/ (\w*) $ }sx              or
+          ( undef, $re, $opts ) = $regex =~ m,^ m([^\w\s]) (.+) \1 (\w*) $,sx
+    )
     {
         $usable_regex = length $opts ? "(?$opts)$re" : $re;
     }
 
     return $usable_regex;
-};
+}
+
+sub _is_qr {
+    my $regex = shift;
+
+    # is_regexp() checks for regexes in a robust manner, say if they're
+    # blessed.
+    return re::is_regexp($regex) if defined &re::is_regexp;
+    return ref $regex eq 'Regexp';
+}
 
 sub _regex_ok {
-    my($self, $this, $regex, $cmp, $name) = @_;
+    my( $self, $this, $regex, $cmp, $name ) = @_;
 
-    my $ok = 0;
+    my $ok           = 0;
     my $usable_regex = $self->maybe_regex($regex);
-    unless (defined $usable_regex) {
+    unless( defined $usable_regex ) {
+        local $Level = $Level + 1;
         $ok = $self->ok( 0, $name );
         $self->diag("    '$regex' doesn't look much like a regex to me.");
         return $ok;
     }
 
     {
+        ## no critic (BuiltinFunctions::ProhibitStringyEval)
+
         my $test;
-        my $code = $self->_caller_context;
+        my $context = $self->_caller_context;
 
-        local($@, $!, $SIG{__DIE__}); # isolate eval
+        local( $@, $!, $SIG{__DIE__} );    # isolate eval
 
-        # Yes, it has to look like this or 5.4.5 won't see the #line directive.
-        # Don't ask me, man, I just work here.
-        $test = eval "
-$code" . q{$test = $this =~ /$usable_regex/ ? 1 : 0};
+        $test = eval $context . q{$test = $this =~ /$usable_regex/ ? 1 : 0};
 
         $test = !$test if $cmp eq '!~';
 
@@ -674,10 +943,12 @@ $code" . q{$test = $this =~ /$usable_regex/ ? 1 : 0};
         $ok = $self->ok( $test, $name );
     }
 
-    unless( $ok ) {
+    unless($ok) {
         $this = defined $this ? "'$this'" : 'undef';
         my $match = $cmp eq '=~' ? "doesn't match" : "matches";
-        $self->diag(sprintf <<DIAGNOSTIC, $this, $match, $regex);
+
+        local $Level = $Level + 1;
+        $self->diag( sprintf <<'DIAGNOSTIC', $this, $match, $regex );
                   %s
     %13s '%s'
 DIAGNOSTIC
@@ -687,43 +958,47 @@ DIAGNOSTIC
     return $ok;
 }
 
-
 # I'm not ready to publish this.  It doesn't deal with array return
 # values from the code or context.
 
-#line 1000
+#line 1389
 
 sub _try {
-    my($self, $code) = @_;
-    
-    local $!;               # eval can mess up $!
-    local $@;               # don't set $@ in the test
-    local $SIG{__DIE__};    # don't trip an outside DIE handler.
-    my $return = eval { $code->() };
-    
-    return wantarray ? ($return, $@) : $return;
+    my( $self, $code, %opts ) = @_;
+
+    my $error;
+    my $return;
+    {
+        local $!;               # eval can mess up $!
+        local $@;               # don't set $@ in the test
+        local $SIG{__DIE__};    # don't trip an outside DIE handler.
+        $return = eval { $code->() };
+        $error = $@;
+    }
+
+    die $error if $error and $opts{die_on_fail};
+
+    return wantarray ? ( $return, $error ) : $return;
 }
 
-#line 1022
+#line 1418
 
 sub is_fh {
-    my $self = shift;
+    my $self     = shift;
     my $maybe_fh = shift;
     return 0 unless defined $maybe_fh;
 
-    return 1 if ref $maybe_fh  eq 'GLOB'; # its a glob ref
-    return 1 if ref \$maybe_fh eq 'GLOB'; # its a glob
+    return 1 if ref $maybe_fh  eq 'GLOB';    # its a glob ref
+    return 1 if ref \$maybe_fh eq 'GLOB';    # its a glob
 
     return eval { $maybe_fh->isa("IO::Handle") } ||
-           # 5.5.4's tied() and can() doesn't like getting undef
-           eval { (tied($maybe_fh) || '')->can('TIEHANDLE') };
+           eval { tied($maybe_fh)->can('TIEHANDLE') };
 }
 
-
-#line 1067
+#line 1461
 
 sub level {
-    my($self, $level) = @_;
+    my( $self, $level ) = @_;
 
     if( defined $level ) {
         $Level = $level;
@@ -731,11 +1006,10 @@ sub level {
     return $Level;
 }
 
-
-#line 1100
+#line 1493
 
 sub use_numbers {
-    my($self, $use_nums) = @_;
+    my( $self, $use_nums ) = @_;
 
     if( defined $use_nums ) {
         $self->{Use_Nums} = $use_nums;
@@ -743,14 +1017,13 @@ sub use_numbers {
     return $self->{Use_Nums};
 }
 
-
-#line 1134
+#line 1526
 
 foreach my $attribute (qw(No_Header No_Ending No_Diag)) {
     my $method = lc $attribute;
 
     my $code = sub {
-        my($self, $no) = @_;
+        my( $self, $no ) = @_;
 
         if( defined $no ) {
             $self->{$attribute} = $no;
@@ -758,15 +1031,35 @@ foreach my $attribute (qw(No_Header No_Ending No_Diag)) {
         return $self->{$attribute};
     };
 
-    no strict 'refs';
-    *{__PACKAGE__.'::'.$method} = $code;
+    no strict 'refs';    ## no critic
+    *{ __PACKAGE__ . '::' . $method } = $code;
 }
 
-
-#line 1188
+#line 1579
 
 sub diag {
-    my($self, @msgs) = @_;
+    my $self = shift;
+
+    $self->_print_comment( $self->_diag_fh, @_ );
+}
+
+#line 1594
+
+sub note {
+    my $self = shift;
+
+    $self->_print_comment( $self->output, @_ );
+}
+
+sub _diag_fh {
+    my $self = shift;
+
+    local $Level = $Level + 1;
+    return $self->in_todo ? $self->todo_output : $self->failure_output;
+}
+
+sub _print_comment {
+    my( $self, $fh, @msgs ) = @_;
 
     return if $self->no_diag;
     return unless @msgs;
@@ -778,22 +1071,43 @@ sub diag {
     # Convert undef to 'undef' so its readable.
     my $msg = join '', map { defined($_) ? $_ : 'undef' } @msgs;
 
-    # Escape each line with a #.
-    $msg =~ s/^/# /gm;
-
-    # Stick a newline on the end if it needs it.
-    $msg .= "\n" unless $msg =~ /\n\Z/;
+    # Escape the beginning, _print will take care of the rest.
+    $msg =~ s/^/# /;
 
     local $Level = $Level + 1;
-    $self->_print_diag($msg);
+    $self->_print_to_fh( $fh, $msg );
 
     return 0;
 }
 
-#line 1225
+#line 1644
+
+sub explain {
+    my $self = shift;
+
+    return map {
+        ref $_
+          ? do {
+            $self->_try(sub { require Data::Dumper }, die_on_fail => 1);
+
+            my $dumper = Data::Dumper->new( [$_] );
+            $dumper->Indent(1)->Terse(1);
+            $dumper->Sortkeys(1) if $dumper->can("Sortkeys");
+            $dumper->Dump;
+          }
+          : $_
+    } @_;
+}
+
+#line 1673
 
 sub _print {
-    my($self, @msgs) = @_;
+    my $self = shift;
+    return $self->_print_to_fh( $self->output, @_ );
+}
+
+sub _print_to_fh {
+    my( $self, $fh, @msgs ) = @_;
 
     # Prevent printing headers when only compiling.  Mostly for when
     # tests are deparsed with B::Deparse
@@ -801,33 +1115,22 @@ sub _print {
 
     my $msg = join '', @msgs;
 
-    local($\, $", $,) = (undef, ' ', '');
-    my $fh = $self->output;
+    local( $\, $", $, ) = ( undef, ' ', '' );
 
     # Escape each line after the first with a # so we don't
     # confuse Test::Harness.
-    $msg =~ s/\n(.)/\n# $1/sg;
+    $msg =~ s{\n(?!\z)}{\n# }sg;
 
     # Stick a newline on the end if it needs it.
-    $msg .= "\n" unless $msg =~ /\n\Z/;
+    $msg .= "\n" unless $msg =~ /\n\z/;
 
-    print $fh $msg;
+    return print $fh $self->_indent, $msg;
 }
 
-#line 1259
-
-sub _print_diag {
-    my $self = shift;
-
-    local($\, $", $,) = (undef, ' ', '');
-    my $fh = $self->todo ? $self->todo_output : $self->failure_output;
-    print $fh @_;
-}    
-
-#line 1296
+#line 1732
 
 sub output {
-    my($self, $fh) = @_;
+    my( $self, $fh ) = @_;
 
     if( defined $fh ) {
         $self->{Out_FH} = $self->_new_fh($fh);
@@ -836,7 +1139,7 @@ sub output {
 }
 
 sub failure_output {
-    my($self, $fh) = @_;
+    my( $self, $fh ) = @_;
 
     if( defined $fh ) {
         $self->{Fail_FH} = $self->_new_fh($fh);
@@ -845,7 +1148,7 @@ sub failure_output {
 }
 
 sub todo_output {
-    my($self, $fh) = @_;
+    my( $self, $fh ) = @_;
 
     if( defined $fh ) {
         $self->{Todo_FH} = $self->_new_fh($fh);
@@ -853,7 +1156,6 @@ sub todo_output {
     return $self->{Todo_FH};
 }
 
-
 sub _new_fh {
     my $self = shift;
     my($file_or_fh) = shift;
@@ -862,24 +1164,37 @@ sub _new_fh {
     if( $self->is_fh($file_or_fh) ) {
         $fh = $file_or_fh;
     }
+    elsif( ref $file_or_fh eq 'SCALAR' ) {
+        # Scalar refs as filehandles was added in 5.8.
+        if( $] >= 5.008 ) {
+            open $fh, ">>", $file_or_fh
+              or $self->croak("Can't open scalar ref $file_or_fh: $!");
+        }
+        # Emulate scalar ref filehandles with a tie.
+        else {
+            $fh = Test::Builder::IO::Scalar->new($file_or_fh)
+              or $self->croak("Can't tie scalar ref $file_or_fh");
+        }
+    }
     else {
-        $fh = do { local *FH };
-        open $fh, ">$file_or_fh" or
-            $self->croak("Can't open test output log $file_or_fh: $!");
-	_autoflush($fh);
+        open $fh, ">", $file_or_fh
+          or $self->croak("Can't open test output log $file_or_fh: $!");
+        _autoflush($fh);
     }
 
     return $fh;
 }
 
-
 sub _autoflush {
     my($fh) = shift;
     my $old_fh = select $fh;
     $| = 1;
     select $old_fh;
+
+    return;
 }
 
+my( $Testout, $Testerr );
 
 sub _dup_stdhandles {
     my $self = shift;
@@ -888,82 +1203,105 @@ sub _dup_stdhandles {
 
     # Set everything to unbuffered else plain prints to STDOUT will
     # come out in the wrong order from our own prints.
-    _autoflush(\*TESTOUT);
-    _autoflush(\*STDOUT);
-    _autoflush(\*TESTERR);
-    _autoflush(\*STDERR);
+    _autoflush($Testout);
+    _autoflush( \*STDOUT );
+    _autoflush($Testerr);
+    _autoflush( \*STDERR );
 
-    $self->output(\*TESTOUT);
-    $self->failure_output(\*TESTERR);
-    $self->todo_output(\*TESTOUT);
-}
+    $self->reset_outputs;
 
+    return;
+}
 
-my $Opened_Testhandles = 0;
 sub _open_testhandles {
-    return if $Opened_Testhandles;
+    my $self = shift;
+
+    return if $self->{Opened_Testhandles};
+
     # We dup STDOUT and STDERR so people can change them in their
     # test suites while still getting normal test output.
-    open(TESTOUT, ">&STDOUT") or die "Can't dup STDOUT:  $!";
-    open(TESTERR, ">&STDERR") or die "Can't dup STDERR:  $!";
-    $Opened_Testhandles = 1;
+    open( $Testout, ">&STDOUT" ) or die "Can't dup STDOUT:  $!";
+    open( $Testerr, ">&STDERR" ) or die "Can't dup STDERR:  $!";
+
+    #    $self->_copy_io_layers( \*STDOUT, $Testout );
+    #    $self->_copy_io_layers( \*STDERR, $Testerr );
+
+    $self->{Opened_Testhandles} = 1;
+
+    return;
+}
+
+sub _copy_io_layers {
+    my( $self, $src, $dst ) = @_;
+
+    $self->_try(
+        sub {
+            require PerlIO;
+            my @src_layers = PerlIO::get_layers($src);
+
+            binmode $dst, join " ", map ":$_", @src_layers if @src_layers;
+        }
+    );
+
+    return;
 }
 
+#line 1857
 
-#line 1396
+sub reset_outputs {
+    my $self = shift;
+
+    $self->output        ($Testout);
+    $self->failure_output($Testerr);
+    $self->todo_output   ($Testout);
+
+    return;
+}
+
+#line 1883
 
 sub _message_at_caller {
     my $self = shift;
 
     local $Level = $Level + 1;
-    my($pack, $file, $line) = $self->caller;
-    return join("", @_) . " at $file line $line.\n";
+    my( $pack, $file, $line ) = $self->caller;
+    return join( "", @_ ) . " at $file line $line.\n";
 }
 
 sub carp {
     my $self = shift;
-    warn $self->_message_at_caller(@_);
+    return warn $self->_message_at_caller(@_);
 }
 
 sub croak {
     my $self = shift;
-    die $self->_message_at_caller(@_);
+    return die $self->_message_at_caller(@_);
 }
 
-sub _plan_check {
-    my $self = shift;
-
-    unless( $self->{Have_Plan} ) {
-        local $Level = $Level + 2;
-        $self->croak("You tried to run a test without a plan");
-    }
-}
 
-#line 1444
+#line 1923
 
 sub current_test {
-    my($self, $num) = @_;
+    my( $self, $num ) = @_;
 
-    lock($self->{Curr_Test});
+    lock( $self->{Curr_Test} );
     if( defined $num ) {
-        unless( $self->{Have_Plan} ) {
-            $self->croak("Can't change the current test number without a plan!");
-        }
-
         $self->{Curr_Test} = $num;
 
         # If the test counter is being pushed forward fill in the details.
         my $test_results = $self->{Test_Results};
         if( $num > @$test_results ) {
             my $start = @$test_results ? @$test_results : 0;
-            for ($start..$num-1) {
-                $test_results->[$_] = &share({
-                    'ok'      => 1, 
-                    actual_ok => undef, 
-                    reason    => 'incrementing test number', 
-                    type      => 'unknown', 
-                    name      => undef 
-                });
+            for( $start .. $num - 1 ) {
+                $test_results->[$_] = &share(
+                    {
+                        'ok'      => 1,
+                        actual_ok => undef,
+                        reason    => 'incrementing test number',
+                        type      => 'unknown',
+                        name      => undef
+                    }
+                );
             }
         }
         # If backward, wipe history.  Its their funeral.
@@ -974,8 +1312,20 @@ sub current_test {
     return $self->{Curr_Test};
 }
 
+#line 1971
+
+sub is_passing {
+    my $self = shift;
+
+    if( @_ ) {
+        $self->{Is_Passing} = shift;
+    }
+
+    return $self->{Is_Passing};
+}
+
 
-#line 1489
+#line 1993
 
 sub summary {
     my($self) = shift;
@@ -983,143 +1333,200 @@ sub summary {
     return map { $_->{'ok'} } @{ $self->{Test_Results} };
 }
 
-#line 1544
+#line 2048
 
 sub details {
     my $self = shift;
     return @{ $self->{Test_Results} };
 }
 
-#line 1569
+#line 2077
 
 sub todo {
-    my($self, $pack) = @_;
+    my( $self, $pack ) = @_;
+
+    return $self->{Todo} if defined $self->{Todo};
 
-    $pack = $pack || $self->exported_to || $self->caller($Level);
-    return 0 unless $pack;
+    local $Level = $Level + 1;
+    my $todo = $self->find_TODO($pack);
+    return $todo if defined $todo;
 
-    no strict 'refs';
-    return defined ${$pack.'::TODO'} ? ${$pack.'::TODO'}
-                                     : 0;
+    return '';
 }
 
-#line 1590
+#line 2099
+
+sub find_TODO {
+    my( $self, $pack ) = @_;
+
+    $pack = $pack || $self->caller(1) || $self->exported_to;
+    return unless $pack;
+
+    no strict 'refs';    ## no critic
+    return ${ $pack . '::TODO' };
+}
+
+#line 2117
+
+sub in_todo {
+    my $self = shift;
 
-sub caller {
-    my($self, $height) = @_;
+    local $Level = $Level + 1;
+    return( defined $self->{Todo} || $self->find_TODO ) ? 1 : 0;
+}
+
+#line 2167
+
+sub todo_start {
+    my $self = shift;
+    my $message = @_ ? shift : '';
+
+    $self->{Start_Todo}++;
+    if( $self->in_todo ) {
+        push @{ $self->{Todo_Stack} } => $self->todo;
+    }
+    $self->{Todo} = $message;
+
+    return;
+}
+
+#line 2189
+
+sub todo_end {
+    my $self = shift;
+
+    if( !$self->{Start_Todo} ) {
+        $self->croak('todo_end() called without todo_start()');
+    }
+
+    $self->{Start_Todo}--;
+
+    if( $self->{Start_Todo} && @{ $self->{Todo_Stack} } ) {
+        $self->{Todo} = pop @{ $self->{Todo_Stack} };
+    }
+    else {
+        delete $self->{Todo};
+    }
+
+    return;
+}
+
+#line 2222
+
+sub caller {    ## no critic (Subroutines::ProhibitBuiltinHomonyms)
+    my( $self, $height ) = @_;
     $height ||= 0;
 
-    my @caller = CORE::caller($self->level + $height + 1);
+    my $level = $self->level + $height + 1;
+    my @caller;
+    do {
+        @caller = CORE::caller( $level );
+        $level--;
+    } until @caller;
     return wantarray ? @caller : $caller[0];
 }
 
-#line 1602
+#line 2239
 
-#line 1616
+#line 2253
 
 #'#
 sub _sanity_check {
     my $self = shift;
 
-    $self->_whoa($self->{Curr_Test} < 0,  'Says here you ran a negative number of tests!');
-    $self->_whoa(!$self->{Have_Plan} and $self->{Curr_Test}, 
-          'Somehow your tests ran without a plan!');
-    $self->_whoa($self->{Curr_Test} != @{ $self->{Test_Results} },
-          'Somehow you got a different number of results than tests ran!');
+    $self->_whoa( $self->{Curr_Test} < 0, 'Says here you ran a negative number of tests!' );
+    $self->_whoa( $self->{Curr_Test} != @{ $self->{Test_Results} },
+        'Somehow you got a different number of results than tests ran!' );
+
+    return;
 }
 
-#line 1637
+#line 2274
 
 sub _whoa {
-    my($self, $check, $desc) = @_;
-    if( $check ) {
+    my( $self, $check, $desc ) = @_;
+    if($check) {
         local $Level = $Level + 1;
         $self->croak(<<"WHOA");
 WHOA!  $desc
 This should never happen!  Please contact the author immediately!
 WHOA
     }
+
+    return;
 }
 
-#line 1659
+#line 2298
 
 sub _my_exit {
-    $? = $_[0];
+    $? = $_[0];    ## no critic (Variables::RequireLocalizedPunctuationVars)
 
     return 1;
 }
 
-
-#line 1672
-
-$SIG{__DIE__} = sub {
-    # We don't want to muck with death in an eval, but $^S isn't
-    # totally reliable.  5.005_03 and 5.6.1 both do the wrong thing
-    # with it.  Instead, we use caller.  This also means it runs under
-    # 5.004!
-    my $in_eval = 0;
-    for( my $stack = 1;  my $sub = (CORE::caller($stack))[3];  $stack++ ) {
-        $in_eval = 1 if $sub =~ /^\(eval\)/;
-    }
-    $Test->{Test_Died} = 1 unless $in_eval;
-};
+#line 2310
 
 sub _ending {
     my $self = shift;
+    return if $self->no_ending;
+    return if $self->{Ending}++;
 
-    $self->_sanity_check();
+    my $real_exit_code = $?;
 
     # Don't bother with an ending if this is a forked copy.  Only the parent
     # should do the ending.
-    # Exit if plan() was never called.  This is so "require Test::Simple" 
+    if( $self->{Original_Pid} != $$ ) {
+        return;
+    }
+
+    # Ran tests but never declared a plan or hit done_testing
+    if( !$self->{Have_Plan} and $self->{Curr_Test} ) {
+        $self->is_passing(0);
+        $self->diag("Tests were run but no plan was declared and done_testing() was not seen.");
+    }
+
+    # Exit if plan() was never called.  This is so "require Test::Simple"
     # doesn't puke.
-    # Don't do an ending if we bailed out.
-    if( ($self->{Original_Pid} != $$) 			or
-	(!$self->{Have_Plan} && !$self->{Test_Died}) 	or
-	$self->{Bailed_Out}
-      )
-    {
-	_my_exit($?);
-	return;
+    if( !$self->{Have_Plan} ) {
+        return;
     }
 
+    # Don't do an ending if we bailed out.
+    if( $self->{Bailed_Out} ) {
+        $self->is_passing(0);
+        return;
+    }
     # Figure out if we passed or failed and print helpful messages.
     my $test_results = $self->{Test_Results};
-    if( @$test_results ) {
+    if(@$test_results) {
         # The plan?  We have no plan.
         if( $self->{No_Plan} ) {
-            $self->_print("1..$self->{Curr_Test}\n") unless $self->no_header;
+            $self->_output_plan($self->{Curr_Test}) unless $self->no_header;
             $self->{Expected_Tests} = $self->{Curr_Test};
         }
 
         # Auto-extended arrays and elements which aren't explicitly
         # filled in with a shared reference will puke under 5.8.0
         # ithreads.  So we have to fill them in by hand. :(
-        my $empty_result = &share({});
-        for my $idx ( 0..$self->{Expected_Tests}-1 ) {
+        my $empty_result = &share( {} );
+        for my $idx ( 0 .. $self->{Expected_Tests} - 1 ) {
             $test_results->[$idx] = $empty_result
               unless defined $test_results->[$idx];
         }
 
-        my $num_failed = grep !$_->{'ok'}, 
-                              @{$test_results}[0..$self->{Curr_Test}-1];
+        my $num_failed = grep !$_->{'ok'}, @{$test_results}[ 0 .. $self->{Curr_Test} - 1 ];
 
         my $num_extra = $self->{Curr_Test} - $self->{Expected_Tests};
 
-        if( $num_extra < 0 ) {
-            my $s = $self->{Expected_Tests} == 1 ? '' : 's';
-            $self->diag(<<"FAIL");
-Looks like you planned $self->{Expected_Tests} test$s but only ran $self->{Curr_Test}.
-FAIL
-        }
-        elsif( $num_extra > 0 ) {
+        if( $num_extra != 0 ) {
             my $s = $self->{Expected_Tests} == 1 ? '' : 's';
             $self->diag(<<"FAIL");
-Looks like you planned $self->{Expected_Tests} test$s but ran $num_extra extra.
+Looks like you planned $self->{Expected_Tests} test$s but ran $self->{Curr_Test}.
 FAIL
+            $self->is_passing(0);
         }
 
-        if ( $num_failed ) {
+        if($num_failed) {
             my $num_tests = $self->{Curr_Test};
             my $s = $num_failed == 1 ? '' : 's';
 
@@ -1128,18 +1535,19 @@ FAIL
             $self->diag(<<"FAIL");
 Looks like you failed $num_failed test$s of $num_tests$qualifier.
 FAIL
+            $self->is_passing(0);
         }
 
-        if( $self->{Test_Died} ) {
+        if($real_exit_code) {
             $self->diag(<<"FAIL");
-Looks like your test died just after $self->{Curr_Test}.
+Looks like your test exited with $real_exit_code just after $self->{Curr_Test}.
 FAIL
-
-            _my_exit( 255 ) && return;
+            $self->is_passing(0);
+            _my_exit($real_exit_code) && return;
         }
 
         my $exit_code;
-        if( $num_failed ) {
+        if($num_failed) {
             $exit_code = $num_failed <= 254 ? $num_failed : 254;
         }
         elsif( $num_extra != 0 ) {
@@ -1149,27 +1557,33 @@ FAIL
             $exit_code = 0;
         }
 
-        _my_exit( $exit_code ) && return;
+        _my_exit($exit_code) && return;
     }
-    elsif ( $self->{Skip_All} ) {
-        _my_exit( 0 ) && return;
+    elsif( $self->{Skip_All} ) {
+        _my_exit(0) && return;
     }
-    elsif ( $self->{Test_Died} ) {
-        $self->diag(<<'FAIL');
-Looks like your test died before it could output anything.
+    elsif($real_exit_code) {
+        $self->diag(<<"FAIL");
+Looks like your test exited with $real_exit_code before it could output anything.
 FAIL
-        _my_exit( 255 ) && return;
+        $self->is_passing(0);
+        _my_exit($real_exit_code) && return;
     }
     else {
         $self->diag("No tests run!\n");
-        _my_exit( 255 ) && return;
+        $self->is_passing(0);
+        _my_exit(255) && return;
     }
+
+    $self->is_passing(0);
+    $self->_whoa( 1, "We fell off the end of _ending()" );
 }
 
 END {
-    $Test->_ending if defined $Test and !$Test->no_ending;
+    $Test->_ending if defined $Test;
 }
 
-#line 1847
+#line 2498
 
 1;
+
@@ -1,50 +1,51 @@
 #line 1
 package Test::More;
 
-use 5.004;
-
+use 5.006;
 use strict;
+use warnings;
+
+#---- perlcritic exemptions. ----#
 
+# We use a lot of subroutine prototypes
+## no critic (Subroutines::ProhibitSubroutinePrototypes)
 
 # Can't use Carp because it might cause use_ok() to accidentally succeed
 # even though the module being used forgot to use Carp.  Yes, this
 # actually happened.
 sub _carp {
-    my($file, $line) = (caller(1))[1,2];
-    warn @_, " at $file line $line\n";
+    my( $file, $line ) = ( caller(1) )[ 1, 2 ];
+    return warn @_, " at $file line $line\n";
 }
 
-
-
-use vars qw($VERSION @ISA @EXPORT %EXPORT_TAGS $TODO);
-$VERSION = '0.72';
-$VERSION = eval $VERSION;    # make the alpha version come out as a number
+our $VERSION = '0.94';
+$VERSION = eval $VERSION;    ## no critic (BuiltinFunctions::ProhibitStringyEval)
 
 use Test::Builder::Module;
-@ISA    = qw(Test::Builder::Module);
-@EXPORT = qw(ok use_ok require_ok
-             is isnt like unlike is_deeply
-             cmp_ok
-             skip todo todo_skip
-             pass fail
-             eq_array eq_hash eq_set
-             $TODO
-             plan
-             can_ok  isa_ok
-             diag
-	     BAIL_OUT
-            );
-
-
-#line 157
+our @ISA    = qw(Test::Builder::Module);
+our @EXPORT = qw(ok use_ok require_ok
+  is isnt like unlike is_deeply
+  cmp_ok
+  skip todo todo_skip
+  pass fail
+  eq_array eq_hash eq_set
+  $TODO
+  plan
+  done_testing
+  can_ok isa_ok new_ok
+  diag note explain
+  subtest
+  BAIL_OUT
+);
+
+#line 164
 
 sub plan {
     my $tb = Test::More->builder;
 
-    $tb->plan(@_);
+    return $tb->plan(@_);
 }
 
-
 # This implements "use Test::More 'no_diag'" but the behavior is
 # deprecated.
 sub import_extra {
@@ -52,7 +53,7 @@ sub import_extra {
     my $list  = shift;
 
     my @other = ();
-    my $idx = 0;
+    my $idx   = 0;
     while( $idx <= $#{$list} ) {
         my $item = $list->[$idx];
 
@@ -67,76 +68,80 @@ sub import_extra {
     }
 
     @$list = @other;
+
+    return;
 }
 
+#line 217
+
+sub done_testing {
+    my $tb = Test::More->builder;
+    $tb->done_testing(@_);
+}
 
-#line 257
+#line 289
 
 sub ok ($;$) {
-    my($test, $name) = @_;
+    my( $test, $name ) = @_;
     my $tb = Test::More->builder;
 
-    $tb->ok($test, $name);
+    return $tb->ok( $test, $name );
 }
 
-#line 324
+#line 367
 
 sub is ($$;$) {
     my $tb = Test::More->builder;
 
-    $tb->is_eq(@_);
+    return $tb->is_eq(@_);
 }
 
 sub isnt ($$;$) {
     my $tb = Test::More->builder;
 
-    $tb->isnt_eq(@_);
+    return $tb->isnt_eq(@_);
 }
 
 *isn't = \&isnt;
 
-
-#line 369
+#line 411
 
 sub like ($$;$) {
     my $tb = Test::More->builder;
 
-    $tb->like(@_);
+    return $tb->like(@_);
 }
 
-
-#line 385
+#line 426
 
 sub unlike ($$;$) {
     my $tb = Test::More->builder;
 
-    $tb->unlike(@_);
+    return $tb->unlike(@_);
 }
 
-
-#line 425
+#line 471
 
 sub cmp_ok($$$;$) {
     my $tb = Test::More->builder;
 
-    $tb->cmp_ok(@_);
+    return $tb->cmp_ok(@_);
 }
 
-
-#line 461
+#line 506
 
 sub can_ok ($@) {
-    my($proto, @methods) = @_;
+    my( $proto, @methods ) = @_;
     my $class = ref $proto || $proto;
     my $tb = Test::More->builder;
 
-    unless( $class ) {
+    unless($class) {
         my $ok = $tb->ok( 0, "->can(...)" );
         $tb->diag('    can_ok() called with empty class or reference');
         return $ok;
     }
 
-    unless( @methods ) {
+    unless(@methods) {
         my $ok = $tb->ok( 0, "$class->can(...)" );
         $tb->diag('    can_ok() called with no methods');
         return $ok;
@@ -144,63 +149,69 @@ sub can_ok ($@) {
 
     my @nok = ();
     foreach my $method (@methods) {
-        $tb->_try(sub { $proto->can($method) }) or push @nok, $method;
+        $tb->_try( sub { $proto->can($method) } ) or push @nok, $method;
     }
 
-    my $name;
-    $name = @methods == 1 ? "$class->can('$methods[0]')" 
-                          : "$class->can(...)";
+    my $name = (@methods == 1) ? "$class->can('$methods[0]')" :
+                                 "$class->can(...)"           ;
 
     my $ok = $tb->ok( !@nok, $name );
 
-    $tb->diag(map "    $class->can('$_') failed\n", @nok);
+    $tb->diag( map "    $class->can('$_') failed\n", @nok );
 
     return $ok;
 }
 
-#line 523
+#line 572
 
 sub isa_ok ($$;$) {
-    my($object, $class, $obj_name) = @_;
+    my( $object, $class, $obj_name ) = @_;
     my $tb = Test::More->builder;
 
     my $diag;
-    $obj_name = 'The object' unless defined $obj_name;
-    my $name = "$obj_name isa $class";
+
     if( !defined $object ) {
+        $obj_name = 'The thing' unless defined $obj_name;
         $diag = "$obj_name isn't defined";
     }
-    elsif( !ref $object ) {
-        $diag = "$obj_name isn't a reference";
-    }
     else {
+        my $whatami = ref $object ? 'object' : 'class';
         # We can't use UNIVERSAL::isa because we want to honor isa() overrides
-        my($rslt, $error) = $tb->_try(sub { $object->isa($class) });
-        if( $error ) {
+        my( $rslt, $error ) = $tb->_try( sub { $object->isa($class) } );
+        if($error) {
             if( $error =~ /^Can't call method "isa" on unblessed reference/ ) {
                 # Its an unblessed reference
-                if( !UNIVERSAL::isa($object, $class) ) {
+                $obj_name = 'The reference' unless defined $obj_name;
+                if( !UNIVERSAL::isa( $object, $class ) ) {
                     my $ref = ref $object;
                     $diag = "$obj_name isn't a '$class' it's a '$ref'";
                 }
-            } else {
+            }
+            elsif( $error =~ /Can't call method "isa" without a package/ ) {
+                # It's something that can't even be a class
+                $obj_name = 'The thing' unless defined $obj_name;
+                $diag = "$obj_name isn't a class or reference";
+            }
+            else {
                 die <<WHOA;
-WHOA! I tried to call ->isa on your object and got some weird error.
+WHOA! I tried to call ->isa on your $whatami and got some weird error.
 Here's the error.
 $error
 WHOA
             }
         }
-        elsif( !$rslt ) {
-            my $ref = ref $object;
-            $diag = "$obj_name isn't a '$class' it's a '$ref'";
+        else {
+            $obj_name = "The $whatami" unless defined $obj_name;
+            if( !$rslt ) {
+                my $ref = ref $object;
+                $diag = "$obj_name isn't a '$class' it's a '$ref'";
+            }
         }
     }
-            
-      
 
+    my $name = "$obj_name isa $class";
     my $ok;
-    if( $diag ) {
+    if($diag) {
         $ok = $tb->ok( 0, $name );
         $tb->diag("    $diag\n");
     }
@@ -211,54 +222,91 @@ WHOA
     return $ok;
 }
 
+#line 651
 
-#line 592
+sub new_ok {
+    my $tb = Test::More->builder;
+    $tb->croak("new_ok() must be given at least a class") unless @_;
+
+    my( $class, $args, $object_name ) = @_;
+
+    $args ||= [];
+    $object_name = "The object" unless defined $object_name;
+
+    my $obj;
+    my( $success, $error ) = $tb->_try( sub { $obj = $class->new(@$args); 1 } );
+    if($success) {
+        local $Test::Builder::Level = $Test::Builder::Level + 1;
+        isa_ok $obj, $class, $object_name;
+    }
+    else {
+        $tb->ok( 0, "new() died" );
+        $tb->diag("    Error was:  $error");
+    }
+
+    return $obj;
+}
+
+#line 719
+
+sub subtest($&) {
+    my ($name, $subtests) = @_;
+
+    my $tb = Test::More->builder;
+    return $tb->subtest(@_);
+}
+
+#line 743
 
 sub pass (;$) {
     my $tb = Test::More->builder;
-    $tb->ok(1, @_);
+
+    return $tb->ok( 1, @_ );
 }
 
 sub fail (;$) {
     my $tb = Test::More->builder;
-    $tb->ok(0, @_);
+
+    return $tb->ok( 0, @_ );
 }
 
-#line 653
+#line 806
 
 sub use_ok ($;@) {
-    my($module, @imports) = @_;
+    my( $module, @imports ) = @_;
     @imports = () unless @imports;
     my $tb = Test::More->builder;
 
-    my($pack,$filename,$line) = caller;
-
-    local($@,$!,$SIG{__DIE__});   # isolate eval
+    my( $pack, $filename, $line ) = caller;
 
+    my $code;
     if( @imports == 1 and $imports[0] =~ /^\d+(?:\.\d+)?$/ ) {
         # probably a version check.  Perl needs to see the bare number
         # for it to work with non-Exporter based modules.
-        eval <<USE;
+        $code = <<USE;
 package $pack;
 use $module $imports[0];
+1;
 USE
     }
     else {
-        eval <<USE;
+        $code = <<USE;
 package $pack;
-use $module \@imports;
+use $module \@{\$args[0]};
+1;
 USE
     }
 
-    my $ok = $tb->ok( !$@, "use $module;" );
+    my( $eval_result, $eval_error ) = _eval( $code, \@imports );
+    my $ok = $tb->ok( $eval_result, "use $module;" );
 
-    unless( $ok ) {
-        chomp $@;
+    unless($ok) {
+        chomp $eval_error;
         $@ =~ s{^BEGIN failed--compilation aborted at .*$}
                 {BEGIN failed--compilation aborted at $filename line $line.}m;
         $tb->diag(<<DIAGNOSTIC);
     Tried to use '$module'.
-    Error:  $@
+    Error:  $eval_error
 DIAGNOSTIC
 
     }
@@ -266,7 +314,25 @@ DIAGNOSTIC
     return $ok;
 }
 
-#line 702
+sub _eval {
+    my( $code, @args ) = @_;
+
+    # Work around oddities surrounding resetting of $@ by immediately
+    # storing it.
+    my( $sigdie, $eval_result, $eval_error );
+    {
+        local( $@, $!, $SIG{__DIE__} );    # isolate eval
+        $eval_result = eval $code;              ## no critic (BuiltinFunctions::ProhibitStringyEval)
+        $eval_error  = $@;
+        $sigdie      = $SIG{__DIE__} || undef;
+    }
+    # make sure that $code got a chance to set $SIG{__DIE__}
+    $SIG{__DIE__} = $sigdie if defined $sigdie;
+
+    return( $eval_result, $eval_error );
+}
+
+#line 875
 
 sub require_ok ($) {
     my($module) = shift;
@@ -278,20 +344,20 @@ sub require_ok ($) {
     # Module names must be barewords, files not.
     $module = qq['$module'] unless _is_module_name($module);
 
-    local($!, $@, $SIG{__DIE__}); # isolate eval
-    local $SIG{__DIE__};
-    eval <<REQUIRE;
+    my $code = <<REQUIRE;
 package $pack;
 require $module;
+1;
 REQUIRE
 
-    my $ok = $tb->ok( !$@, "require $module;" );
+    my( $eval_result, $eval_error ) = _eval($code);
+    my $ok = $tb->ok( $eval_result, "require $module;" );
 
-    unless( $ok ) {
-        chomp $@;
+    unless($ok) {
+        chomp $eval_error;
         $tb->diag(<<DIAGNOSTIC);
     Tried to require '$module'.
-    Error:  $@
+    Error:  $eval_error
 DIAGNOSTIC
 
     }
@@ -299,7 +365,6 @@ DIAGNOSTIC
     return $ok;
 }
 
-
 sub _is_module_name {
     my $module = shift;
 
@@ -307,55 +372,56 @@ sub _is_module_name {
     # End with an alphanumeric.
     # The rest is an alphanumeric or ::
     $module =~ s/\b::\b//g;
-    $module =~ /^[a-zA-Z]\w*$/;
+
+    return $module =~ /^[a-zA-Z]\w*$/ ? 1 : 0;
 }
 
-#line 779
+#line 952
 
-use vars qw(@Data_Stack %Refs_Seen);
+our( @Data_Stack, %Refs_Seen );
 my $DNE = bless [], 'Does::Not::Exist';
 
 sub _dne {
-    ref $_[0] eq ref $DNE;
+    return ref $_[0] eq ref $DNE;
 }
 
-
+## no critic (Subroutines::RequireArgUnpacking)
 sub is_deeply {
     my $tb = Test::More->builder;
 
     unless( @_ == 2 or @_ == 3 ) {
-        my $msg = <<WARNING;
+        my $msg = <<'WARNING';
 is_deeply() takes two or three args, you gave %d.
 This usually means you passed an array or hash instead 
 of a reference to it
 WARNING
-        chop $msg;   # clip off newline so carp() will put in line/file
+        chop $msg;    # clip off newline so carp() will put in line/file
 
         _carp sprintf $msg, scalar @_;
 
-	return $tb->ok(0);
+        return $tb->ok(0);
     }
 
-    my($got, $expected, $name) = @_;
+    my( $got, $expected, $name ) = @_;
 
-    $tb->_unoverload_str(\$expected, \$got);
+    $tb->_unoverload_str( \$expected, \$got );
 
     my $ok;
-    if( !ref $got and !ref $expected ) {  		# neither is a reference
-        $ok = $tb->is_eq($got, $expected, $name);
+    if( !ref $got and !ref $expected ) {    # neither is a reference
+        $ok = $tb->is_eq( $got, $expected, $name );
     }
-    elsif( !ref $got xor !ref $expected ) {  	# one's a reference, one isn't
-        $ok = $tb->ok(0, $name);
-	$tb->diag( _format_stack({ vals => [ $got, $expected ] }) );
+    elsif( !ref $got xor !ref $expected ) {    # one's a reference, one isn't
+        $ok = $tb->ok( 0, $name );
+        $tb->diag( _format_stack({ vals => [ $got, $expected ] }) );
     }
-    else {			       		# both references
+    else {                                     # both references
         local @Data_Stack = ();
-        if( _deep_check($got, $expected) ) {
-            $ok = $tb->ok(1, $name);
+        if( _deep_check( $got, $expected ) ) {
+            $ok = $tb->ok( 1, $name );
         }
         else {
-            $ok = $tb->ok(0, $name);
-            $tb->diag(_format_stack(@Data_Stack));
+            $ok = $tb->ok( 0, $name );
+            $tb->diag( _format_stack(@Data_Stack) );
         }
     }
 
@@ -365,11 +431,11 @@ WARNING
 sub _format_stack {
     my(@Stack) = @_;
 
-    my $var = '$FOO';
+    my $var       = '$FOO';
     my $did_arrow = 0;
     foreach my $entry (@Stack) {
         my $type = $entry->{type} || '';
-        my $idx  = $entry->{'idx'};
+        my $idx = $entry->{'idx'};
         if( $type eq 'HASH' ) {
             $var .= "->" unless $did_arrow++;
             $var .= "{$idx}";
@@ -383,18 +449,19 @@ sub _format_stack {
         }
     }
 
-    my @vals = @{$Stack[-1]{vals}}[0,1];
+    my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ];
     my @vars = ();
-    ($vars[0] = $var) =~ s/\$FOO/     \$got/;
-    ($vars[1] = $var) =~ s/\$FOO/\$expected/;
+    ( $vars[0] = $var ) =~ s/\$FOO/     \$got/;
+    ( $vars[1] = $var ) =~ s/\$FOO/\$expected/;
 
     my $out = "Structures begin differing at:\n";
-    foreach my $idx (0..$#vals) {
+    foreach my $idx ( 0 .. $#vals ) {
         my $val = $vals[$idx];
-        $vals[$idx] = !defined $val ? 'undef'          :
-                      _dne($val)    ? "Does not exist" :
-                      ref $val      ? "$val"           :
-                                      "'$val'";
+        $vals[$idx]
+          = !defined $val ? 'undef'
+          : _dne($val)    ? "Does not exist"
+          : ref $val      ? "$val"
+          :                 "'$val'";
     }
 
     $out .= "$vars[0] = $vals[0]\n";
@@ -404,33 +471,39 @@ sub _format_stack {
     return $out;
 }
 
-
 sub _type {
     my $thing = shift;
 
     return '' if !ref $thing;
 
     for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) {
-        return $type if UNIVERSAL::isa($thing, $type);
+        return $type if UNIVERSAL::isa( $thing, $type );
     }
 
     return '';
 }
 
-#line 925
+#line 1112
 
 sub diag {
-    my $tb = Test::More->builder;
+    return Test::More->builder->diag(@_);
+}
 
-    $tb->diag(@_);
+sub note {
+    return Test::More->builder->note(@_);
 }
 
+#line 1138
 
-#line 994
+sub explain {
+    return Test::More->builder->explain(@_);
+}
 
-#'#
+#line 1204
+
+## no critic (Subroutines::RequireFinalReturn)
 sub skip {
-    my($why, $how_many) = @_;
+    my( $why, $how_many ) = @_;
     my $tb = Test::More->builder;
 
     unless( defined $how_many ) {
@@ -441,23 +514,23 @@ sub skip {
     }
 
     if( defined $how_many and $how_many =~ /\D/ ) {
-        _carp "skip() was passed a non-numeric number of tests.  Did you get the arguments backwards?";
+        _carp
+          "skip() was passed a non-numeric number of tests.  Did you get the arguments backwards?";
         $how_many = 1;
     }
 
-    for( 1..$how_many ) {
+    for( 1 .. $how_many ) {
         $tb->skip($why);
     }
 
-    local $^W = 0;
+    no warnings 'exiting';
     last SKIP;
 }
 
-
-#line 1081
+#line 1288
 
 sub todo_skip {
-    my($why, $how_many) = @_;
+    my( $why, $how_many ) = @_;
     my $tb = Test::More->builder;
 
     unless( defined $how_many ) {
@@ -467,35 +540,35 @@ sub todo_skip {
         $how_many = 1;
     }
 
-    for( 1..$how_many ) {
+    for( 1 .. $how_many ) {
         $tb->todo_skip($why);
     }
 
-    local $^W = 0;
+    no warnings 'exiting';
     last TODO;
 }
 
-#line 1134
+#line 1343
 
 sub BAIL_OUT {
     my $reason = shift;
-    my $tb = Test::More->builder;
+    my $tb     = Test::More->builder;
 
     $tb->BAIL_OUT($reason);
 }
 
-#line 1173
+#line 1382
 
 #'#
 sub eq_array {
-    local @Data_Stack;
+    local @Data_Stack = ();
     _deep_check(@_);
 }
 
-sub _eq_array  {
-    my($a1, $a2) = @_;
+sub _eq_array {
+    my( $a1, $a2 ) = @_;
 
-    if( grep !_type($_) eq 'ARRAY', $a1, $a2 ) {
+    if( grep _type($_) ne 'ARRAY', $a1, $a2 ) {
         warn "eq_array passed a non-array ref";
         return 0;
     }
@@ -504,12 +577,12 @@ sub _eq_array  {
 
     my $ok = 1;
     my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2;
-    for (0..$max) {
+    for( 0 .. $max ) {
         my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_];
         my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_];
 
-        push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [$e1, $e2] };
-        $ok = _deep_check($e1,$e2);
+        push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] };
+        $ok = _deep_check( $e1, $e2 );
         pop @Data_Stack if $ok;
 
         last unless $ok;
@@ -519,7 +592,7 @@ sub _eq_array  {
 }
 
 sub _deep_check {
-    my($e1, $e2) = @_;
+    my( $e1, $e2 ) = @_;
     my $tb = Test::More->builder;
 
     my $ok = 0;
@@ -531,27 +604,31 @@ sub _deep_check {
 
     {
         # Quiet uninitialized value warnings when comparing undefs.
-        local $^W = 0; 
+        no warnings 'uninitialized';
 
-        $tb->_unoverload_str(\$e1, \$e2);
+        $tb->_unoverload_str( \$e1, \$e2 );
 
         # Either they're both references or both not.
-        my $same_ref = !(!ref $e1 xor !ref $e2);
-	my $not_ref  = (!ref $e1 and !ref $e2);
+        my $same_ref = !( !ref $e1 xor !ref $e2 );
+        my $not_ref = ( !ref $e1 and !ref $e2 );
 
         if( defined $e1 xor defined $e2 ) {
             $ok = 0;
         }
-        elsif ( _dne($e1) xor _dne($e2) ) {
+        elsif( !defined $e1 and !defined $e2 ) {
+            # Shortcut if they're both defined.
+            $ok = 1;
+        }
+        elsif( _dne($e1) xor _dne($e2) ) {
             $ok = 0;
         }
-        elsif ( $same_ref and ($e1 eq $e2) ) {
+        elsif( $same_ref and( $e1 eq $e2 ) ) {
             $ok = 1;
         }
-	elsif ( $not_ref ) {
-	    push @Data_Stack, { type => '', vals => [$e1, $e2] };
-	    $ok = 0;
-	}
+        elsif($not_ref) {
+            push @Data_Stack, { type => '', vals => [ $e1, $e2 ] };
+            $ok = 0;
+        }
         else {
             if( $Refs_Seen{$e1} ) {
                 return $Refs_Seen{$e1} eq $e2;
@@ -564,61 +641,59 @@ sub _deep_check {
             $type = 'DIFFERENT' unless _type($e2) eq $type;
 
             if( $type eq 'DIFFERENT' ) {
-                push @Data_Stack, { type => $type, vals => [$e1, $e2] };
+                push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
                 $ok = 0;
             }
             elsif( $type eq 'ARRAY' ) {
-                $ok = _eq_array($e1, $e2);
+                $ok = _eq_array( $e1, $e2 );
             }
             elsif( $type eq 'HASH' ) {
-                $ok = _eq_hash($e1, $e2);
+                $ok = _eq_hash( $e1, $e2 );
             }
             elsif( $type eq 'REF' ) {
-                push @Data_Stack, { type => $type, vals => [$e1, $e2] };
-                $ok = _deep_check($$e1, $$e2);
+                push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
+                $ok = _deep_check( $$e1, $$e2 );
                 pop @Data_Stack if $ok;
             }
             elsif( $type eq 'SCALAR' ) {
-                push @Data_Stack, { type => 'REF', vals => [$e1, $e2] };
-                $ok = _deep_check($$e1, $$e2);
+                push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] };
+                $ok = _deep_check( $$e1, $$e2 );
                 pop @Data_Stack if $ok;
             }
-            elsif( $type ) {
-                push @Data_Stack, { type => $type, vals => [$e1, $e2] };
+            elsif($type) {
+                push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
                 $ok = 0;
             }
-	    else {
-		_whoa(1, "No type in _deep_check");
-	    }
+            else {
+                _whoa( 1, "No type in _deep_check" );
+            }
         }
     }
 
     return $ok;
 }
 
-
 sub _whoa {
-    my($check, $desc) = @_;
-    if( $check ) {
-        die <<WHOA;
+    my( $check, $desc ) = @_;
+    if($check) {
+        die <<"WHOA";
 WHOA!  $desc
 This should never happen!  Please contact the author immediately!
 WHOA
     }
 }
 
-
-#line 1304
+#line 1515
 
 sub eq_hash {
-    local @Data_Stack;
+    local @Data_Stack = ();
     return _deep_check(@_);
 }
 
 sub _eq_hash {
-    my($a1, $a2) = @_;
+    my( $a1, $a2 ) = @_;
 
-    if( grep !_type($_) eq 'HASH', $a1, $a2 ) {
+    if( grep _type($_) ne 'HASH', $a1, $a2 ) {
         warn "eq_hash passed a non-hash ref";
         return 0;
     }
@@ -627,12 +702,12 @@ sub _eq_hash {
 
     my $ok = 1;
     my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2;
-    foreach my $k (keys %$bigger) {
+    foreach my $k ( keys %$bigger ) {
         my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE;
         my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE;
 
-        push @Data_Stack, { type => 'HASH', idx => $k, vals => [$e1, $e2] };
-        $ok = _deep_check($e1, $e2);
+        push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] };
+        $ok = _deep_check( $e1, $e2 );
         pop @Data_Stack if $ok;
 
         last unless $ok;
@@ -641,16 +716,15 @@ sub _eq_hash {
     return $ok;
 }
 
-#line 1361
+#line 1572
 
-sub eq_set  {
-    my($a1, $a2) = @_;
+sub eq_set {
+    my( $a1, $a2 ) = @_;
     return 0 unless @$a1 == @$a2;
 
-    # There's faster ways to do this, but this is easiest.
-    local $^W = 0;
+    no warnings 'uninitialized';
 
-    # It really doesn't matter how we sort them, as long as both arrays are 
+    # It really doesn't matter how we sort them, as long as both arrays are
     # sorted with the same algorithm.
     #
     # Ensure that references are not accidentally treated the same as a
@@ -662,11 +736,11 @@ sub eq_set  {
     # I don't know how references would be sorted so we just don't sort
     # them.  This means eq_set doesn't really work with refs.
     return eq_array(
-           [grep(ref, @$a1), sort( grep(!ref, @$a1) )],
-           [grep(ref, @$a2), sort( grep(!ref, @$a2) )],
+        [ grep( ref, @$a1 ), sort( grep( !ref, @$a1 ) ) ],
+        [ grep( ref, @$a2 ), sort( grep( !ref, @$a2 ) ) ],
     );
 }
 
-#line 1551
+#line 1774
 
 1;
@@ -12,7 +12,7 @@ our $INJAVASCRIPT = 0;
 sub template {
     my ($class, $block) = @_;
 
-    return "sub { return '' }" unless $block =~ /\S/;
+    return "function() { return ''; }" unless $block =~ /\S/;
 
     return <<"...";
 function(context) {
@@ -33,6 +33,17 @@ $block
 ...
 }
  
+# Try to do 1 .. 10 expansions
+sub _attempt_range_expand_val ($) {
+    my $val = shift;
+    return $val unless
+        my ( $from, $to ) = $val =~ m/\s*\[\s*(\S+)\s*\.\.\s*(\S+)\s*\]/;
+
+    die "Range expansion is current supported for positive/negative integer values only (e.g. [ 1 .. 10 ])\nCannot expand: $val" unless $from =~ m/^-?\d+$/ && $to =~ m/^-?\d+$/;
+
+    return join '', '[', join( ',', $from .. $to ), ']';
+}
+
 #------------------------------------------------------------------------
 # textblock($text)
 #------------------------------------------------------------------------
@@ -101,6 +112,7 @@ sub assign {
             $var = '[' . join(', ', @$var) . ']';
         }
     }
+    $val =  _attempt_range_expand_val $val;
     $val .= ', 1' if $default;
     return "stash.set($var, $val)";
 }
@@ -277,6 +289,8 @@ sub foreach {
         $loop_restore = 'stash = context.delocalise()';
     }
 
+    $list = _attempt_range_expand_val $list;
+
     return <<EOF;
 
 // FOREACH 
@@ -529,6 +543,24 @@ sub use {
 
 
 #------------------------------------------------------------------------
+# raw(\@lnameargs)                         [% RAW alias = plugin(args) %]
+#     # => [ [$file, ...], \@args, $alias ]
+#------------------------------------------------------------------------
+
+sub raw {
+    my ($class, $lnameargs) = @_;
+    my ($file, $args, $alias) = @$lnameargs;
+    $file = shift @$file;       # same production rule as INCLUDE
+    $alias ||= $file;
+    $args = &args($class, $args);
+#    $file .= ", $args" if $args;
+    $file =~ s/'|"//g;
+    return "// RAW\n"
+         . "stash.set($alias, $file);";
+}
+
+
+#------------------------------------------------------------------------
 # stubs()                                                      [% STOP %]
 #------------------------------------------------------------------------
 
@@ -561,12 +593,100 @@ sub quoted {
     return "throw('QUOTED called with unknown arguments in Jemplate');";
 }   
 
+#------------------------------------------------------------------------
+# macro($name, $block, \@args)
+#------------------------------------------------------------------------
+
 sub macro {
-    return "throw('MACRO not yet supported in Jemplate');";
-}   
+    my ($class, $ident, $block, $args) = @_;
+
+    if ($args) {
+        $args = join(';', map { "args['$_'] = fargs.shift()" } @$args);
+
+        return <<EOF;
+
+//MACRO
+stash.set('$ident', function () {
+    var output = '';
+    var args = {};
+    var fargs = Array.prototype.slice.call(arguments);
+    $args;
+    args.arguments = Array.prototype.slice.call(arguments);
+
+    var params = fargs.shift() || {};
+
+    for (var key in params) {
+        args[key] = params[key];
+    }
+
+    context.stash.clone(args);
+    try {
+$block
+    }
+    catch(e) {
+        var error = context.set_error(e, output);
+        throw(error);
+    }
+
+    context.stash.declone();
+    return output;
+});
+
+EOF
+
+    }
+    else {
+        return <<EOF;
+
+//MACRO
+
+stash.set('$ident', function () {
+    var output = '';
+    var args = {};
+    
+    var fargs = Array.prototype.slice.call(arguments);
+    args.arguments = Array.prototype.slice.call(arguments);   
+    
+    if (typeof arguments[0] == 'object') args = arguments[0];
+    
+    context.stash.clone(args);
+    try {
+$block
+    }
+    catch(e) {
+        var error = context.set_error(e, output);
+        throw(error);
+    }
+
+    context.stash.declone(); 
+    return output;});
+
+EOF
+    }
+}
 
 sub capture {
-    return "throw('CAPTURE not yet supported in Jemplate');";
+    my ($class, $name, $block) = @_;
+
+    if (ref $name) {
+        if (scalar @$name == 2 && ! $name->[1]) {
+            $name = $name->[0];
+        }
+        else {
+            $name = '[' . join(', ', @$name) . ']';
+        }
+    }
+
+    return <<EOF;
+
+// CAPTURE
+(function() {
+	var output = '';
+	$block
+	stash.set($name, output);
+})();
+EOF
+
 }   
 
 BEGIN {
@@ -49,7 +49,7 @@ my ($factory, $rawstart);
 
 @RESERVED = qw( 
 	GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER BLOCK END
-	USE PLUGIN FILTER MACRO JAVASCRIPT TO STEP AND OR NOT DIV MOD
+	USE RAW PLUGIN FILTER MACRO JAVASCRIPT TO STEP AND OR NOT DIV MOD
 	IF UNLESS ELSE ELSIF FOR NEXT WHILE SWITCH CASE META IN
 	TRY THROW CATCH FINAL LAST RETURN STOP CLEAR VIEW DEBUG
     );
@@ -140,187 +140,189 @@ $STATES = [
 	{#State 0
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
-			'node' => 23,
-			'term' => 58,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'template' => 51,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'template' => 53,
 			'defblockname' => 14,
-			'filter' => 29,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'switch' => 34,
-			'try' => 35,
 			'assign' => 19,
-			'block' => 72,
-			'directive' => 71,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'lterm' => 58,
+			'node' => 23,
+			'term' => 60,
+			'expr' => 64,
+			'use' => 65,
+			'defblock' => 68,
+			'filter' => 30,
+			'sterm' => 70,
+			'chunks' => 34,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
+			'directive' => 73,
+			'block' => 74,
+			'raw' => 39,
+			'condition' => 75
 		}
 	},
 	{#State 1
 		ACTIONS => {
-			"\$" => 42,
-			'LITERAL' => 75,
+			"\$" => 44,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'setlist' => 76,
-			'item' => 39,
+			'setlist' => 78,
+			'item' => 41,
 			'assign' => 19,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
 	{#State 2
-		DEFAULT => -129
+		DEFAULT => -131
 	},
 	{#State 3
-		DEFAULT => -25
+		DEFAULT => -26
 	},
 	{#State 4
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 79,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 81,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
 	{#State 5
-		DEFAULT => -23
+		DEFAULT => -24
 	},
 	{#State 6
 		ACTIONS => {
-			";" => 80
+			";" => 82
 		}
 	},
 	{#State 7
-		DEFAULT => -38
+		DEFAULT => -39
 	},
 	{#State 8
-		DEFAULT => -14
+		DEFAULT => -15
 	},
 	{#State 9
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 90,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 92,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
 	{#State 10
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"]" => 94,
-			"\${" => 37
+			"]" => 96,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 96,
-			'item' => 39,
-			'range' => 93,
+			'sterm' => 98,
+			'item' => 41,
+			'range' => 95,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 95,
-			'lterm' => 56,
-			'list' => 92
+			'ident' => 79,
+			'term' => 97,
+			'lterm' => 58,
+			'list' => 94
 		}
 	},
 	{#State 11
 		ACTIONS => {
-			";" => 97
+			";" => 99
 		}
 	},
 	{#State 12
@@ -328,1200 +330,1209 @@ $STATES = [
 	},
 	{#State 13
 		ACTIONS => {
-			";" => -20
+			";" => -21
 		},
-		DEFAULT => -28
+		DEFAULT => -29
 	},
 	{#State 14
 		ACTIONS => {
-			'IDENT' => 98
+			'IDENT' => 100
 		},
-		DEFAULT => -86,
+		DEFAULT => -88,
 		GOTOS => {
-			'blockargs' => 101,
-			'metadata' => 100,
-			'meta' => 99
+			'blockargs' => 103,
+			'metadata' => 102,
+			'meta' => 101
 		}
 	},
 	{#State 15
 		ACTIONS => {
-			'IDENT' => 98
+			'IDENT' => 100
 		},
 		GOTOS => {
-			'metadata' => 102,
-			'meta' => 99
+			'metadata' => 104,
+			'meta' => 101
 		}
 	},
 	{#State 16
 		ACTIONS => {
-			'DOT' => 103,
-			'ASSIGN' => 104
+			'DOT' => 105,
+			'ASSIGN' => 106
 		},
-		DEFAULT => -108
+		DEFAULT => -110
 	},
 	{#State 17
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 105,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 107,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
 	{#State 18
 		ACTIONS => {
-			'IDENT' => 106
+			'IDENT' => 108
 		}
 	},
 	{#State 19
-		DEFAULT => -148
+		DEFAULT => -150
 	},
 	{#State 20
 		DEFAULT => -12
 	},
 	{#State 21
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
-			'IDENT' => 107,
-			"\"" => 60,
-			"\$" => 42,
+			"{" => 31,
+			'LITERAL' => 80,
+			'IDENT' => 109,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
-			'loopvar' => 109,
+			'sterm' => 70,
+			'item' => 41,
+			'loopvar' => 111,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 108,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 110,
+			'lterm' => 58
 		}
 	},
 	{#State 22
-		DEFAULT => -41
+		DEFAULT => -42
 	},
 	{#State 23
-		DEFAULT => -126
+		DEFAULT => -128
 	},
 	{#State 24
 		DEFAULT => -6
 	},
 	{#State 25
 		ACTIONS => {
-			"\"" => 116,
-			"\$" => 113,
-			'LITERAL' => 115,
-			'FILENAME' => 83,
-			'IDENT' => 110,
-			'NUMBER' => 84,
-			"\${" => 37
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
 		GOTOS => {
-			'names' => 91,
-			'lvalue' => 111,
-			'item' => 112,
-			'name' => 82,
-			'filepart' => 87,
-			'filename' => 85,
-			'nameargs' => 117,
-			'lnameargs' => 114
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 116
 		}
 	},
 	{#State 26
-		DEFAULT => -112
+		DEFAULT => -114
 	},
 	{#State 27
 		ACTIONS => {
-			"\$" => 42,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 118
+			'ident' => 120
 		}
 	},
 	{#State 28
 		ACTIONS => {
-			'LITERAL' => 123,
-			'FILENAME' => 83,
-			'IDENT' => 119,
-			'NUMBER' => 84
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
-		DEFAULT => -86,
 		GOTOS => {
-			'blockargs' => 122,
-			'filepart' => 87,
-			'filename' => 121,
-			'blockname' => 120,
-			'metadata' => 100,
-			'meta' => 99
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 121
 		}
 	},
 	{#State 29
-		DEFAULT => -44
+		ACTIONS => {
+			'LITERAL' => 126,
+			'FILENAME' => 85,
+			'IDENT' => 122,
+			'NUMBER' => 86
+		},
+		DEFAULT => -88,
+		GOTOS => {
+			'blockargs' => 125,
+			'filepart' => 89,
+			'filename' => 124,
+			'blockname' => 123,
+			'metadata' => 102,
+			'meta' => 101
+		}
 	},
 	{#State 30
+		DEFAULT => -45
+	},
+	{#State 31
 		ACTIONS => {
-			"\$" => 42,
-			'LITERAL' => 128,
+			"\$" => 44,
+			'LITERAL' => 131,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -118,
+		DEFAULT => -120,
 		GOTOS => {
-			'params' => 127,
-			'hash' => 124,
-			'item' => 125,
-			'param' => 126
+			'params' => 130,
+			'hash' => 127,
+			'item' => 128,
+			'param' => 129
 		}
 	},
-	{#State 31
-		DEFAULT => -26
-	},
 	{#State 32
+		DEFAULT => -27
+	},
+	{#State 33
 		ACTIONS => {
-			"\"" => 116,
-			"\$" => 113,
-			'LITERAL' => 115,
-			'FILENAME' => 83,
-			'IDENT' => 110,
-			'NUMBER' => 84,
-			"\${" => 37
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
 		GOTOS => {
-			'names' => 91,
-			'lvalue' => 111,
-			'item' => 112,
-			'name' => 82,
-			'filepart' => 87,
-			'filename' => 85,
-			'nameargs' => 117,
-			'lnameargs' => 129
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 132
 		}
 	},
-	{#State 33
+	{#State 34
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -2,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
-			'chunk' => 130,
-			'defblock' => 66,
+			'wrapper' => 47,
+			'atomexpr' => 49,
+			'chunk' => 133,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 71,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 34
-		DEFAULT => -22
-	},
 	{#State 35
-		DEFAULT => -24
+		DEFAULT => -23
 	},
 	{#State 36
+		DEFAULT => -25
+	},
+	{#State 37
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 131,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 134,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 37
+	{#State 38
 		ACTIONS => {
-			"\"" => 60,
-			"\$" => 42,
-			'LITERAL' => 78,
+			"\"" => 62,
+			"\$" => 44,
+			'LITERAL' => 80,
 			'IDENT' => 2,
 			'REF' => 27,
 			'NUMBER' => 26,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 132,
-			'item' => 39,
+			'sterm' => 135,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77
+			'ident' => 79
 		}
 	},
-	{#State 38
+	{#State 39
+		DEFAULT => -14
+	},
+	{#State 40
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 133,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 136,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 39
+	{#State 41
 		ACTIONS => {
-			"(" => 134
+			"(" => 137
 		},
-		DEFAULT => -127
+		DEFAULT => -129
 	},
-	{#State 40
-		DEFAULT => -39
+	{#State 42
+		DEFAULT => -40
 	},
-	{#State 41
+	{#State 43
 		DEFAULT => -11
 	},
-	{#State 42
+	{#State 44
 		ACTIONS => {
-			'IDENT' => 135
+			'IDENT' => 138
 		}
 	},
-	{#State 43
+	{#State 45
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 136,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 139,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 44
+	{#State 46
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 137,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 140,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 45
-		DEFAULT => -43
+	{#State 47
+		DEFAULT => -44
 	},
-	{#State 46
+	{#State 48
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 138,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 141,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 47
+	{#State 49
 		ACTIONS => {
-			'IF' => 142,
-			'FILTER' => 141,
-			'FOR' => 140,
-			'WHILE' => 144,
-			'WRAPPER' => 143,
-			'UNLESS' => 139
+			'IF' => 145,
+			'FILTER' => 144,
+			'FOR' => 143,
+			'WHILE' => 147,
+			'WRAPPER' => 146,
+			'UNLESS' => 142
 		}
 	},
-	{#State 48
-		DEFAULT => -40
+	{#State 50
+		DEFAULT => -41
 	},
-	{#State 49
+	{#State 51
 		DEFAULT => -10
 	},
-	{#State 50
+	{#State 52
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 145,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 148,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 51
+	{#State 53
 		ACTIONS => {
-			'' => 146
+			'' => 149
 		}
 	},
-	{#State 52
+	{#State 54
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 57,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 59,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 149,
-			'sterm' => 68,
-			'item' => 39,
-			'assign' => 148,
+			'expr' => 152,
+			'sterm' => 70,
+			'item' => 41,
+			'assign' => 151,
 			'node' => 23,
-			'ident' => 147,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 150,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 53
+	{#State 55
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 150,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 153,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 54
+	{#State 56
 		ACTIONS => {
-			";" => 151
+			";" => 154
 		}
 	},
-	{#State 55
+	{#State 57
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 152,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 155,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 56
-		DEFAULT => -102
+	{#State 58
+		DEFAULT => -104
 	},
-	{#State 57
+	{#State 59
 		ACTIONS => {
-			'ASSIGN' => 153
+			'ASSIGN' => 156
 		},
-		DEFAULT => -111
+		DEFAULT => -113
 	},
-	{#State 58
-		DEFAULT => -145
+	{#State 60
+		DEFAULT => -147
 	},
-	{#State 59
-		DEFAULT => -15
+	{#State 61
+		DEFAULT => -16
 	},
-	{#State 60
-		DEFAULT => -175,
+	{#State 62
+		DEFAULT => -177,
 		GOTOS => {
-			'quoted' => 154
+			'quoted' => 157
 		}
 	},
-	{#State 61
+	{#State 63
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 155,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 158,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 62
+	{#State 64
 		ACTIONS => {
-			";" => -16,
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			";" => -17,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -27
+		DEFAULT => -28
 	},
-	{#State 63
+	{#State 65
 		DEFAULT => -13
 	},
-	{#State 64
-		DEFAULT => -37
+	{#State 66
+		DEFAULT => -38
 	},
-	{#State 65
+	{#State 67
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 166,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 169,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 66
+	{#State 68
 		DEFAULT => -9
 	},
-	{#State 67
+	{#State 69
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 167,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 170,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 68
-		DEFAULT => -103
+	{#State 70
+		DEFAULT => -105
 	},
-	{#State 69
+	{#State 71
 		ACTIONS => {
-			"\$" => 42,
-			'LITERAL' => 75,
+			"\$" => 44,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'setlist' => 168,
-			'item' => 39,
+			'setlist' => 171,
+			'item' => 41,
 			'assign' => 19,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 70
+	{#State 72
 		ACTIONS => {
-			"\$" => 42,
-			'COMMA' => 170,
-			'LITERAL' => 75,
+			"\$" => 44,
+			'COMMA' => 173,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -19,
+		DEFAULT => -20,
 		GOTOS => {
-			'item' => 39,
-			'assign' => 169,
+			'item' => 41,
+			'assign' => 172,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 71
+	{#State 73
 		DEFAULT => -8
 	},
-	{#State 72
+	{#State 74
 		DEFAULT => -1
 	},
-	{#State 73
-		DEFAULT => -21
+	{#State 75
+		DEFAULT => -22
 	},
-	{#State 74
+	{#State 76
 		ACTIONS => {
-			'ASSIGN' => 171,
-			'DOT' => 103
+			'ASSIGN' => 174,
+			'DOT' => 105
 		}
 	},
-	{#State 75
+	{#State 77
 		ACTIONS => {
-			'ASSIGN' => 153
+			'ASSIGN' => 156
 		}
 	},
-	{#State 76
+	{#State 78
 		ACTIONS => {
-			'COMMA' => 170,
-			'LITERAL' => 75,
+			'COMMA' => 173,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\$" => 42,
-			"\${" => 37
+			"\$" => 44,
+			"\${" => 38
 		},
-		DEFAULT => -31,
+		DEFAULT => -32,
 		GOTOS => {
-			'item' => 39,
-			'assign' => 169,
+			'item' => 41,
+			'assign' => 172,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 77
-		ACTIONS => {
-			'DOT' => 103
-		},
-		DEFAULT => -108
-	},
-	{#State 78
-		DEFAULT => -111
-	},
 	{#State 79
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 172,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
-		}
+			'DOT' => 105
+		},
+		DEFAULT => -110
 	},
 	{#State 80
-		DEFAULT => -7
+		DEFAULT => -113
 	},
 	{#State 81
-		DEFAULT => -172
+		ACTIONS => {
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 175,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
+		}
 	},
 	{#State 82
-		DEFAULT => -165
+		DEFAULT => -7
 	},
 	{#State 83
-		DEFAULT => -171
+		DEFAULT => -174
 	},
 	{#State 84
-		DEFAULT => -173
+		DEFAULT => -167
 	},
 	{#State 85
+		DEFAULT => -173
+	},
+	{#State 86
+		DEFAULT => -175
+	},
+	{#State 87
 		ACTIONS => {
-			'DOT' => 173
+			'DOT' => 176
 		},
-		DEFAULT => -167
+		DEFAULT => -169
 	},
-	{#State 86
+	{#State 88
 		ACTIONS => {
-			"\$" => 42,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 174
+			'ident' => 177
 		}
 	},
-	{#State 87
-		DEFAULT => -170
+	{#State 89
+		DEFAULT => -172
 	},
-	{#State 88
-		DEFAULT => -168
+	{#State 90
+		DEFAULT => -170
 	},
-	{#State 89
-		DEFAULT => -175,
+	{#State 91
+		DEFAULT => -177,
 		GOTOS => {
-			'quoted' => 175
+			'quoted' => 178
 		}
 	},
-	{#State 90
-		DEFAULT => -36
+	{#State 92
+		DEFAULT => -37
 	},
-	{#State 91
+	{#State 93
 		ACTIONS => {
-			"+" => 176,
-			"(" => 177
+			"+" => 179,
+			"(" => 180
 		},
-		DEFAULT => -155,
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 178
+			'args' => 181
 		}
 	},
-	{#State 92
+	{#State 94
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 181,
-			'LITERAL' => 78,
+			"{" => 31,
+			'COMMA' => 184,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"]" => 179,
-			"\${" => 37
+			"]" => 182,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 180,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 183,
+			'lterm' => 58
 		}
 	},
-	{#State 93
+	{#State 95
 		ACTIONS => {
-			"]" => 182
+			"]" => 185
 		}
 	},
-	{#State 94
-		DEFAULT => -106
+	{#State 96
+		DEFAULT => -108
 	},
-	{#State 95
-		DEFAULT => -115
+	{#State 97
+		DEFAULT => -117
 	},
-	{#State 96
+	{#State 98
 		ACTIONS => {
-			'TO' => 183
+			'TO' => 186
 		},
-		DEFAULT => -103
+		DEFAULT => -105
 	},
-	{#State 97
+	{#State 99
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 184,
-			'directive' => 71,
+			'block' => 187,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 98
+	{#State 100
 		ACTIONS => {
-			'ASSIGN' => 185
+			'ASSIGN' => 188
 		}
 	},
-	{#State 99
-		DEFAULT => -98
+	{#State 101
+		DEFAULT => -100
 	},
-	{#State 100
+	{#State 102
 		ACTIONS => {
-			'COMMA' => 187,
-			'IDENT' => 98
+			'COMMA' => 190,
+			'IDENT' => 100
 		},
-		DEFAULT => -85,
+		DEFAULT => -87,
 		GOTOS => {
-			'meta' => 186
+			'meta' => 189
 		}
 	},
-	{#State 101
+	{#State 103
 		ACTIONS => {
-			";" => 188
+			";" => 191
 		}
 	},
-	{#State 102
+	{#State 104
 		ACTIONS => {
-			'COMMA' => 187,
-			'IDENT' => 98
+			'COMMA' => 190,
+			'IDENT' => 100
 		},
-		DEFAULT => -17,
+		DEFAULT => -18,
 		GOTOS => {
-			'meta' => 186
+			'meta' => 189
 		}
 	},
-	{#State 103
+	{#State 105
 		ACTIONS => {
-			"\$" => 42,
+			"\$" => 44,
 			'IDENT' => 2,
-			'NUMBER' => 190,
-			"\${" => 37
+			'NUMBER' => 193,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
-			'node' => 189
+			'item' => 41,
+			'node' => 192
 		}
 	},
-	{#State 104
+	{#State 106
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
+			"(" => 54,
+			'SWITCH' => 55,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
 			'FOR' => 21,
 			'NEXT' => 22,
-			'LITERAL' => 57,
-			"\"" => 60,
-			'PROCESS' => 61,
+			'LITERAL' => 59,
+			"\"" => 62,
+			'PROCESS' => 63,
 			'FILTER' => 25,
-			'RETURN' => 64,
-			'INSERT' => 65,
+			'RETURN' => 66,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 191,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			"\${" => 37
+			'WHILE' => 69,
+			'BLOCK' => 194,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'expr' => 193,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'expr' => 196,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'atomdir' => 13,
-			'mdir' => 192,
-			'filter' => 29,
-			'sterm' => 68,
-			'ident' => 147,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'mdir' => 195,
+			'filter' => 30,
+			'sterm' => 70,
+			'ident' => 150,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 194,
-			'condition' => 73,
-			'lterm' => 56
+			'directive' => 197,
+			'condition' => 75,
+			'lterm' => 58
 		}
 	},
-	{#State 105
-		DEFAULT => -34
+	{#State 107
+		DEFAULT => -35
 	},
-	{#State 106
+	{#State 108
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'INCLUDE' => 17,
-			"(" => 196,
-			'SWITCH' => 53,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
+			"(" => 199,
+			'SWITCH' => 55,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
 			'FOR' => 21,
 			'NEXT' => 22,
-			'LITERAL' => 57,
-			"\"" => 60,
-			'PROCESS' => 61,
+			'LITERAL' => 59,
+			"\"" => 62,
+			'PROCESS' => 63,
 			'FILTER' => 25,
-			'RETURN' => 64,
-			'INSERT' => 65,
+			'RETURN' => 66,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 191,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			"\${" => 37
+			'WHILE' => 69,
+			'BLOCK' => 194,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'expr' => 197,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'expr' => 200,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'atomdir' => 13,
-			'mdir' => 195,
-			'filter' => 29,
-			'sterm' => 68,
-			'ident' => 147,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'mdir' => 198,
+			'filter' => 30,
+			'sterm' => 70,
+			'ident' => 150,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 194,
-			'condition' => 73,
-			'lterm' => 56
+			'directive' => 197,
+			'condition' => 75,
+			'lterm' => 58
 		}
 	},
-	{#State 107
+	{#State 109
 		ACTIONS => {
-			'IN' => 199,
-			'ASSIGN' => 198
+			'IN' => 202,
+			'ASSIGN' => 201
 		},
-		DEFAULT => -129
+		DEFAULT => -131
 	},
-	{#State 108
-		DEFAULT => -155,
+	{#State 110
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 200
+			'args' => 203
 		}
 	},
-	{#State 109
+	{#State 111
 		ACTIONS => {
-			";" => 201
+			";" => 204
 		}
 	},
-	{#State 110
+	{#State 112
 		ACTIONS => {
-			'ASSIGN' => -129
+			'ASSIGN' => -131
 		},
-		DEFAULT => -172
+		DEFAULT => -174
 	},
-	{#State 111
+	{#State 113
 		ACTIONS => {
-			'ASSIGN' => 202
+			'ASSIGN' => 205
 		}
 	},
-	{#State 112
-		DEFAULT => -158
+	{#State 114
+		DEFAULT => -160
 	},
-	{#State 113
+	{#State 115
 		ACTIONS => {
-			"\$" => 42,
-			'IDENT' => 203,
-			"\${" => 37
+			"\$" => 44,
+			'IDENT' => 206,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 174
+			'ident' => 177
 		}
 	},
-	{#State 114
+	{#State 116
 		ACTIONS => {
-			";" => 204
+			";" => 207
 		}
 	},
-	{#State 115
+	{#State 117
 		ACTIONS => {
-			'ASSIGN' => -160
+			'ASSIGN' => -162
 		},
-		DEFAULT => -168
+		DEFAULT => -170
 	},
-	{#State 116
-		DEFAULT => -175,
+	{#State 118
+		DEFAULT => -177,
 		GOTOS => {
-			'quoted' => 205
+			'quoted' => 208
 		}
 	},
-	{#State 117
-		DEFAULT => -157
-	},
-	{#State 118
-		ACTIONS => {
-			'DOT' => 103
-		},
-		DEFAULT => -109
-	},
 	{#State 119
-		ACTIONS => {
-			'ASSIGN' => 185
-		},
-		DEFAULT => -172
+		DEFAULT => -159
 	},
 	{#State 120
-		DEFAULT => -82
-	},
-	{#State 121
 		ACTIONS => {
-			'DOT' => 173
+			'DOT' => 105
 		},
-		DEFAULT => -83
+		DEFAULT => -111
+	},
+	{#State 121
+		DEFAULT => -76
 	},
 	{#State 122
 		ACTIONS => {
-			";" => 206
-		}
+			'ASSIGN' => 188
+		},
+		DEFAULT => -174
 	},
 	{#State 123
 		DEFAULT => -84
 	},
 	{#State 124
 		ACTIONS => {
-			"}" => 207
-		}
+			'DOT' => 176
+		},
+		DEFAULT => -85
 	},
 	{#State 125
 		ACTIONS => {
-			'ASSIGN' => 208
+			";" => 209
 		}
 	},
 	{#State 126
-		DEFAULT => -121
+		DEFAULT => -86
 	},
 	{#State 127
 		ACTIONS => {
-			"\$" => 42,
-			'COMMA' => 210,
-			'LITERAL' => 128,
-			'IDENT' => 2,
-			"\${" => 37
-		},
-		DEFAULT => -117,
-		GOTOS => {
-			'item' => 125,
-			'param' => 209
+			"}" => 210
 		}
 	},
 	{#State 128
@@ -1530,3618 +1541,3715 @@ $STATES = [
 		}
 	},
 	{#State 129
-		DEFAULT => -74
+		DEFAULT => -123
 	},
 	{#State 130
-		DEFAULT => -4
+		ACTIONS => {
+			"\$" => 44,
+			'COMMA' => 213,
+			'LITERAL' => 131,
+			'IDENT' => 2,
+			"\${" => 38
+		},
+		DEFAULT => -119,
+		GOTOS => {
+			'item' => 128,
+			'param' => 212
+		}
 	},
 	{#State 131
 		ACTIONS => {
-			";" => 212
+			'ASSIGN' => 214
 		}
 	},
 	{#State 132
+		DEFAULT => -75
+	},
+	{#State 133
+		DEFAULT => -4
+	},
+	{#State 134
 		ACTIONS => {
-			"}" => 213
+			";" => 215
 		}
 	},
-	{#State 133
+	{#State 135
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			"}" => 216
+		}
+	},
+	{#State 136
+		ACTIONS => {
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -141
+		DEFAULT => -143
 	},
-	{#State 134
-		DEFAULT => -155,
+	{#State 137
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 214
+			'args' => 217
 		}
 	},
-	{#State 135
-		DEFAULT => -131
+	{#State 138
+		DEFAULT => -133
 	},
-	{#State 136
+	{#State 139
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 215,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 218,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 137
+	{#State 140
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -30
+		DEFAULT => -31
 	},
-	{#State 138
+	{#State 141
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -29
+		DEFAULT => -30
 	},
-	{#State 139
+	{#State 142
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 216,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 219,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 140
+	{#State 143
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
-			'IDENT' => 107,
-			"\"" => 60,
-			"\$" => 42,
+			"{" => 31,
+			'LITERAL' => 80,
+			'IDENT' => 109,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
-			'loopvar' => 217,
+			'sterm' => 70,
+			'item' => 41,
+			'loopvar' => 220,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 108,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 110,
+			'lterm' => 58
 		}
 	},
-	{#State 141
+	{#State 144
 		ACTIONS => {
-			"\"" => 116,
-			"\$" => 113,
-			'LITERAL' => 115,
-			'FILENAME' => 83,
-			'IDENT' => 110,
-			'NUMBER' => 84,
-			"\${" => 37
+			"\"" => 118,
+			"\$" => 115,
+			'LITERAL' => 117,
+			'FILENAME' => 85,
+			'IDENT' => 112,
+			'NUMBER' => 86,
+			"\${" => 38
 		},
 		GOTOS => {
-			'names' => 91,
-			'lvalue' => 111,
-			'item' => 112,
-			'name' => 82,
-			'filepart' => 87,
-			'filename' => 85,
-			'nameargs' => 117,
-			'lnameargs' => 218
+			'names' => 93,
+			'lvalue' => 113,
+			'item' => 114,
+			'name' => 84,
+			'filepart' => 89,
+			'filename' => 87,
+			'nameargs' => 119,
+			'lnameargs' => 221
 		}
 	},
-	{#State 142
+	{#State 145
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 219,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 222,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 143
+	{#State 146
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 220,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 223,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 144
+	{#State 147
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 221,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 224,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 145
-		DEFAULT => -42
+	{#State 148
+		DEFAULT => -43
 	},
-	{#State 146
+	{#State 149
 		DEFAULT => 0
 	},
-	{#State 147
+	{#State 150
 		ACTIONS => {
-			'DOT' => 103,
-			'ASSIGN' => 171
+			'DOT' => 105,
+			'ASSIGN' => 174
 		},
-		DEFAULT => -108
+		DEFAULT => -110
 	},
-	{#State 148
+	{#State 151
 		ACTIONS => {
-			")" => 222
+			")" => 225
 		}
 	},
-	{#State 149
+	{#State 152
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			")" => 223,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			")" => 226,
+			'OR' => 164
 		}
 	},
-	{#State 150
+	{#State 153
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 224,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 227,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 151
-		DEFAULT => -77,
+	{#State 154
+		DEFAULT => -79,
 		GOTOS => {
-			'@4-2' => 225
+			'@4-2' => 228
 		}
 	},
-	{#State 152
+	{#State 155
 		ACTIONS => {
-			";" => 226
+			";" => 229
 		}
 	},
-	{#State 153
+	{#State 156
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 227,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 230,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 154
+	{#State 157
 		ACTIONS => {
-			"\"" => 232,
-			'TEXT' => 229,
-			";" => 231,
-			"\$" => 42,
+			"\"" => 235,
+			'TEXT' => 232,
+			";" => 234,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 228,
-			'quotable' => 230
+			'ident' => 231,
+			'quotable' => 233
 		}
 	},
-	{#State 155
-		DEFAULT => -35
+	{#State 158
+		DEFAULT => -36
 	},
-	{#State 156
+	{#State 159
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 233,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 236,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 157
+	{#State 160
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 234,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 237,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 158
+	{#State 161
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 235,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 238,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 159
+	{#State 162
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 236,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 239,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 160
+	{#State 163
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 237,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 240,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 161
+	{#State 164
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 238,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 241,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 162
+	{#State 165
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 239,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 242,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 163
+	{#State 166
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 240,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 243,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 164
+	{#State 167
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 241,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 244,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 165
+	{#State 168
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 242,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 245,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 166
-		DEFAULT => -33
+	{#State 169
+		DEFAULT => -34
 	},
-	{#State 167
+	{#State 170
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 243,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 246,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 168
+	{#State 171
 		ACTIONS => {
-			'COMMA' => 170,
-			'LITERAL' => 75,
+			'COMMA' => 173,
+			'LITERAL' => 77,
 			'IDENT' => 2,
-			"\$" => 42,
-			"\${" => 37
+			"\$" => 44,
+			"\${" => 38
 		},
-		DEFAULT => -32,
+		DEFAULT => -33,
 		GOTOS => {
-			'item' => 39,
-			'assign' => 169,
+			'item' => 41,
+			'assign' => 172,
 			'node' => 23,
-			'ident' => 74
+			'ident' => 76
 		}
 	},
-	{#State 169
-		DEFAULT => -146
+	{#State 172
+		DEFAULT => -148
 	},
-	{#State 170
-		DEFAULT => -147
+	{#State 173
+		DEFAULT => -149
 	},
-	{#State 171
+	{#State 174
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 244,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 247,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 172
+	{#State 175
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 245,
-			'directive' => 71,
+			'block' => 248,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 173
+	{#State 176
 		ACTIONS => {
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 246
+			'filepart' => 249
 		}
 	},
-	{#State 174
+	{#State 177
 		ACTIONS => {
-			'DOT' => 103
+			'DOT' => 105
 		},
-		DEFAULT => -155,
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 247
+			'args' => 250
 		}
 	},
-	{#State 175
+	{#State 178
 		ACTIONS => {
-			"\"" => 248,
-			'TEXT' => 229,
-			";" => 231,
-			"\$" => 42,
+			"\"" => 251,
+			'TEXT' => 232,
+			";" => 234,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 228,
-			'quotable' => 230
+			'ident' => 231,
+			'quotable' => 233
 		}
 	},
-	{#State 176
+	{#State 179
 		ACTIONS => {
-			"\"" => 89,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'filename' => 85,
-			'name' => 249
+			'filepart' => 89,
+			'filename' => 87,
+			'name' => 252
 		}
 	},
-	{#State 177
-		DEFAULT => -155,
+	{#State 180
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 250
+			'args' => 253
 		}
 	},
-	{#State 178
+	{#State 181
 		ACTIONS => {
-			'LITERAL' => 254,
+			'NOT' => 40,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"{" => 30,
-			'COMMA' => 256,
-			"\${" => 37
+			"{" => 31,
+			'COMMA' => 259,
+			"(" => 54,
+			"\${" => 38
 		},
-		DEFAULT => -162,
+		DEFAULT => -164,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 179
-		DEFAULT => -104
+	{#State 182
+		DEFAULT => -106
 	},
-	{#State 180
-		DEFAULT => -113
+	{#State 183
+		DEFAULT => -115
 	},
-	{#State 181
-		DEFAULT => -114
+	{#State 184
+		DEFAULT => -116
 	},
-	{#State 182
-		DEFAULT => -105
+	{#State 185
+		DEFAULT => -107
 	},
-	{#State 183
+	{#State 186
 		ACTIONS => {
-			"\"" => 60,
-			"\$" => 42,
-			'LITERAL' => 78,
+			"\"" => 62,
+			"\$" => 44,
+			'LITERAL' => 80,
 			'IDENT' => 2,
 			'REF' => 27,
 			'NUMBER' => 26,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 257,
-			'item' => 39,
+			'sterm' => 260,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77
+			'ident' => 79
 		}
 	},
-	{#State 184
+	{#State 187
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 259
+			'final' => 262
 		}
 	},
-	{#State 185
+	{#State 188
 		ACTIONS => {
-			"\"" => 263,
-			'LITERAL' => 262,
-			'NUMBER' => 261
+			"\"" => 266,
+			'LITERAL' => 265,
+			'NUMBER' => 264
 		}
 	},
-	{#State 186
-		DEFAULT => -96
+	{#State 189
+		DEFAULT => -98
 	},
-	{#State 187
-		DEFAULT => -97
+	{#State 190
+		DEFAULT => -99
 	},
-	{#State 188
+	{#State 191
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
-			'node' => 23,
-			'term' => 58,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'template' => 264,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'template' => 267,
 			'defblockname' => 14,
-			'filter' => 29,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
 			'assign' => 19,
-			'block' => 72,
-			'directive' => 71,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
-		}
-	},
-	{#State 189
-		DEFAULT => -124
-	},
-	{#State 190
-		DEFAULT => -125
-	},
-	{#State 191
-		ACTIONS => {
-			";" => 265
+			'lterm' => 58,
+			'node' => 23,
+			'term' => 60,
+			'expr' => 64,
+			'use' => 65,
+			'defblock' => 68,
+			'filter' => 30,
+			'sterm' => 70,
+			'chunks' => 34,
+			'setlist' => 72,
+			'switch' => 35,
+			'try' => 36,
+			'directive' => 73,
+			'block' => 74,
+			'raw' => 39,
+			'condition' => 75
 		}
 	},
 	{#State 192
-		DEFAULT => -88
+		DEFAULT => -126
 	},
 	{#State 193
-		ACTIONS => {
-			";" => -149,
-			"+" => 156,
-			'LITERAL' => -149,
-			'IDENT' => -149,
-			'CAT' => 162,
-			"\$" => -149,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			'COMMA' => -149,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161,
-			"\${" => -149
-		},
-		DEFAULT => -27
+		DEFAULT => -127
 	},
 	{#State 194
-		DEFAULT => -91
+		ACTIONS => {
+			";" => 268
+		}
 	},
 	{#State 195
 		DEFAULT => -90
 	},
 	{#State 196
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 57,
-			'IDENT' => 266,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
-			"[" => 10,
-			'NUMBER' => 26,
-			'REF' => 27,
-			"\${" => 37
+			";" => -151,
+			"+" => 159,
+			'LITERAL' => -151,
+			'IDENT' => -151,
+			'CAT' => 165,
+			"\$" => -151,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			'COMMA' => -151,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"\${" => -151
 		},
-		GOTOS => {
-			'expr' => 149,
-			'sterm' => 68,
-			'item' => 39,
-			'assign' => 148,
-			'margs' => 267,
-			'node' => 23,
-			'ident' => 147,
-			'term' => 58,
-			'lterm' => 56
-		}
+		DEFAULT => -28
 	},
 	{#State 197
+		DEFAULT => -93
+	},
+	{#State 198
+		DEFAULT => -92
+	},
+	{#State 199
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 59,
+			'IDENT' => 269,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
+			"[" => 10,
+			'NUMBER' => 26,
+			'REF' => 27,
+			"\${" => 38
 		},
-		DEFAULT => -27
+		GOTOS => {
+			'expr' => 152,
+			'sterm' => 70,
+			'item' => 41,
+			'assign' => 151,
+			'margs' => 270,
+			'node' => 23,
+			'ident' => 150,
+			'term' => 60,
+			'lterm' => 58
+		}
 	},
-	{#State 198
+	{#State 200
+		ACTIONS => {
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
+		},
+		DEFAULT => -28
+	},
+	{#State 201
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 268,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 271,
+			'lterm' => 58
 		}
 	},
-	{#State 199
+	{#State 202
 		ACTIONS => {
-			"{" => 30,
-			'LITERAL' => 78,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 269,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 272,
+			'lterm' => 58
 		}
 	},
-	{#State 200
+	{#State 203
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -65,
+		DEFAULT => -66,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 201
-		DEFAULT => -57,
+	{#State 204
+		DEFAULT => -58,
 		GOTOS => {
-			'@1-3' => 270
+			'@1-3' => 273
 		}
 	},
-	{#State 202
+	{#State 205
 		ACTIONS => {
-			"\"" => 89,
-			"\$" => 86,
-			'LITERAL' => 88,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			"\"" => 91,
+			"\$" => 88,
+			'LITERAL' => 90,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'names' => 91,
-			'nameargs' => 271,
-			'filename' => 85,
-			'name' => 82
+			'filepart' => 89,
+			'names' => 93,
+			'nameargs' => 274,
+			'filename' => 87,
+			'name' => 84
 		}
 	},
-	{#State 203
+	{#State 206
 		ACTIONS => {
-			'ASSIGN' => -131
+			'ASSIGN' => -133
 		},
-		DEFAULT => -129
+		DEFAULT => -131
 	},
-	{#State 204
+	{#State 207
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 272,
-			'directive' => 71,
+			'block' => 275,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 205
+	{#State 208
 		ACTIONS => {
-			"\"" => 273,
-			'TEXT' => 229,
-			";" => 231,
-			"\$" => 42,
+			"\"" => 276,
+			'TEXT' => 232,
+			";" => 234,
+			"\$" => 44,
 			'IDENT' => 2,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 228,
-			'quotable' => 230
+			'ident' => 231,
+			'quotable' => 233
 		}
 	},
-	{#State 206
+	{#State 209
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 274,
-			'directive' => 71,
+			'block' => 277,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 207
-		DEFAULT => -107
+	{#State 210
+		DEFAULT => -109
 	},
-	{#State 208
+	{#State 211
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 275,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 278,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 209
-		DEFAULT => -119
+	{#State 212
+		DEFAULT => -121
 	},
-	{#State 210
-		DEFAULT => -120
+	{#State 213
+		DEFAULT => -122
 	},
-	{#State 211
+	{#State 214
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 276,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 279,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 212
-		DEFAULT => -75,
+	{#State 215
+		DEFAULT => -77,
 		GOTOS => {
-			'@3-3' => 277
+			'@3-3' => 280
 		}
 	},
-	{#State 213
-		DEFAULT => -130
+	{#State 216
+		DEFAULT => -132
 	},
-	{#State 214
+	{#State 217
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			")" => 278,
-			"\${" => 37
+			")" => 281,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 215
+	{#State 218
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 279,
-			'directive' => 71,
+			'block' => 282,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 216
+	{#State 219
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -48
+		DEFAULT => -49
 	},
-	{#State 217
-		DEFAULT => -59
+	{#State 220
+		DEFAULT => -60
 	},
-	{#State 218
-		DEFAULT => -80
+	{#State 221
+		DEFAULT => -82
 	},
-	{#State 219
+	{#State 222
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -46
+		DEFAULT => -47
 	},
-	{#State 220
-		DEFAULT => -67
+	{#State 223
+		DEFAULT => -68
 	},
-	{#State 221
+	{#State 224
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -62
+		DEFAULT => -63
 	},
-	{#State 222
-		DEFAULT => -143
+	{#State 225
+		DEFAULT => -145
 	},
-	{#State 223
-		DEFAULT => -144
+	{#State 226
+		DEFAULT => -146
 	},
-	{#State 224
+	{#State 227
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 280,
-			'directive' => 71,
+			'block' => 283,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 225
+	{#State 228
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 281,
-			'directive' => 71,
+			'block' => 284,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 226
+	{#State 229
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 282,
-			'directive' => 71,
+			'block' => 285,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 227
+	{#State 230
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -150
+		DEFAULT => -152
 	},
-	{#State 228
+	{#State 231
 		ACTIONS => {
-			'DOT' => 103
+			'DOT' => 105
 		},
-		DEFAULT => -176
-	},
-	{#State 229
-		DEFAULT => -177
-	},
-	{#State 230
-		DEFAULT => -174
-	},
-	{#State 231
 		DEFAULT => -178
 	},
 	{#State 232
-		DEFAULT => -110
+		DEFAULT => -179
 	},
 	{#State 233
-		ACTIONS => {
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165
-		},
-		DEFAULT => -134
+		DEFAULT => -176
 	},
 	{#State 234
-		ACTIONS => {
-			":" => 283,
-			'CMPOP' => 163,
-			"?" => 157,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
-		}
+		DEFAULT => -180
 	},
 	{#State 235
-		ACTIONS => {
-			'MOD' => 164
-		},
-		DEFAULT => -135
+		DEFAULT => -112
 	},
 	{#State 236
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -139
+		DEFAULT => -136
 	},
 	{#State 237
 		ACTIONS => {
-			"+" => 156,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165
-		},
-		DEFAULT => -132
+			":" => 286,
+			'CMPOP' => 166,
+			"?" => 160,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
+		}
 	},
 	{#State 238
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'MOD' => 167
 		},
-		DEFAULT => -140
+		DEFAULT => -137
 	},
 	{#State 239
 		ACTIONS => {
-			"+" => 156,
-			'CMPOP' => 163,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -138
+		DEFAULT => -141
 	},
 	{#State 240
 		ACTIONS => {
-			"+" => 156,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'BINOP' => 160
+			'DIV' => 161,
+			"+" => 159,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -137
+		DEFAULT => -134
 	},
 	{#State 241
-		DEFAULT => -136
+		ACTIONS => {
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
+		},
+		DEFAULT => -142
 	},
 	{#State 242
 		ACTIONS => {
-			'DIV' => 158,
-			'MOD' => 164
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'CMPOP' => 166,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -133
+		DEFAULT => -140
 	},
 	{#State 243
-		DEFAULT => -60,
+		ACTIONS => {
+			'DIV' => 161,
+			'BINOP' => 163,
+			"+" => 159,
+			'MOD' => 167,
+			"/" => 168
+		},
+		DEFAULT => -139
+	},
+	{#State 244
+		DEFAULT => -138
+	},
+	{#State 245
+		ACTIONS => {
+			'DIV' => 161,
+			'MOD' => 167
+		},
+		DEFAULT => -135
+	},
+	{#State 246
+		DEFAULT => -61,
 		GOTOS => {
-			'@2-3' => 284
+			'@2-3' => 287
 		}
 	},
-	{#State 244
+	{#State 247
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -149
+		DEFAULT => -151
 	},
-	{#State 245
+	{#State 248
 		ACTIONS => {
-			'ELSIF' => 287,
-			'ELSE' => 285
+			'ELSIF' => 290,
+			'ELSE' => 288
 		},
-		DEFAULT => -51,
+		DEFAULT => -52,
 		GOTOS => {
-			'else' => 286
+			'else' => 289
 		}
 	},
-	{#State 246
-		DEFAULT => -169
+	{#State 249
+		DEFAULT => -171
 	},
-	{#State 247
+	{#State 250
 		ACTIONS => {
-			'LITERAL' => 254,
+			'NOT' => 40,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"{" => 30,
-			'COMMA' => 256,
-			"\${" => 37
+			"{" => 31,
+			'COMMA' => 259,
+			"(" => 54,
+			"\${" => 38
 		},
-		DEFAULT => -161,
+		DEFAULT => -163,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 248
-		DEFAULT => -166
+	{#State 251
+		DEFAULT => -168
 	},
-	{#State 249
-		DEFAULT => -164
+	{#State 252
+		DEFAULT => -166
 	},
-	{#State 250
+	{#State 253
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			")" => 288,
-			"\${" => 37
+			")" => 291,
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 251
-		ACTIONS => {
-			'DOT' => 103,
-			'ASSIGN' => 289
-		},
-		DEFAULT => -108
-	},
-	{#State 252
+	{#State 254
 		ACTIONS => {
-			"(" => 134,
-			'ASSIGN' => 208
+			'DOT' => 105,
+			'ASSIGN' => 292
 		},
-		DEFAULT => -127
-	},
-	{#State 253
-		DEFAULT => -152
+		DEFAULT => -110
 	},
-	{#State 254
+	{#State 255
 		ACTIONS => {
+			"(" => 137,
 			'ASSIGN' => 211
 		},
-		DEFAULT => -111
-	},
-	{#State 255
-		DEFAULT => -151
+		DEFAULT => -129
 	},
 	{#State 256
 		DEFAULT => -154
 	},
 	{#State 257
-		DEFAULT => -116
+		ACTIONS => {
+			'ASSIGN' => 214
+		},
+		DEFAULT => -113
 	},
 	{#State 258
 		ACTIONS => {
-			";" => 290
-		}
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
+		},
+		DEFAULT => -153
 	},
 	{#State 259
+		DEFAULT => -156
+	},
+	{#State 260
+		DEFAULT => -118
+	},
+	{#State 261
 		ACTIONS => {
-			'END' => 291
+			";" => 293
 		}
 	},
-	{#State 260
+	{#State 262
+		ACTIONS => {
+			'END' => 294
+		}
+	},
+	{#State 263
 		ACTIONS => {
-			";" => 293,
-			'DEFAULT' => 294,
-			'FILENAME' => 83,
-			'IDENT' => 81,
-			'NUMBER' => 84
+			";" => 296,
+			'DEFAULT' => 297,
+			'FILENAME' => 85,
+			'IDENT' => 83,
+			'NUMBER' => 86
 		},
 		GOTOS => {
-			'filepart' => 87,
-			'filename' => 292
+			'filepart' => 89,
+			'filename' => 295
 		}
 	},
-	{#State 261
-		DEFAULT => -101
+	{#State 264
+		DEFAULT => -103
 	},
-	{#State 262
-		DEFAULT => -99
+	{#State 265
+		DEFAULT => -101
 	},
-	{#State 263
+	{#State 266
 		ACTIONS => {
-			'TEXT' => 295
+			'TEXT' => 298
 		}
 	},
-	{#State 264
+	{#State 267
 		ACTIONS => {
-			'END' => 296
+			'END' => 299
 		}
 	},
-	{#State 265
+	{#State 268
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 297,
-			'directive' => 71,
+			'block' => 300,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 266
+	{#State 269
 		ACTIONS => {
-			'IDENT' => -95,
-			")" => -95,
-			'COMMA' => -95
+			'IDENT' => -97,
+			")" => -97,
+			'COMMA' => -97
 		},
-		DEFAULT => -129
+		DEFAULT => -131
 	},
-	{#State 267
+	{#State 270
 		ACTIONS => {
-			'COMMA' => 300,
-			'IDENT' => 298,
-			")" => 299
+			'COMMA' => 303,
+			'IDENT' => 301,
+			")" => 302
 		}
 	},
-	{#State 268
-		DEFAULT => -155,
+	{#State 271
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 301
+			'args' => 304
 		}
 	},
-	{#State 269
-		DEFAULT => -155,
+	{#State 272
+		DEFAULT => -157,
 		GOTOS => {
-			'args' => 302
+			'args' => 305
 		}
 	},
-	{#State 270
+	{#State 273
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 303,
-			'directive' => 71,
+			'block' => 306,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 271
-		DEFAULT => -156
+	{#State 274
+		DEFAULT => -158
 	},
-	{#State 272
+	{#State 275
 		ACTIONS => {
-			'END' => 304
+			'END' => 307
 		}
 	},
-	{#State 273
+	{#State 276
 		ACTIONS => {
-			'ASSIGN' => -159
+			'ASSIGN' => -161
 		},
-		DEFAULT => -166
+		DEFAULT => -168
 	},
-	{#State 274
+	{#State 277
 		ACTIONS => {
-			'END' => 305
+			'END' => 308
 		}
 	},
-	{#State 275
+	{#State 278
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'DIV' => 161,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -123
+		DEFAULT => -125
 	},
-	{#State 276
+	{#State 279
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'DIV' => 161,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -122
+		DEFAULT => -124
 	},
-	{#State 277
+	{#State 280
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 306,
-			'directive' => 71,
+			'block' => 309,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 278
-		DEFAULT => -128
+	{#State 281
+		DEFAULT => -130
 	},
-	{#State 279
+	{#State 282
 		ACTIONS => {
-			'ELSIF' => 287,
-			'ELSE' => 285
+			'ELSIF' => 290,
+			'ELSE' => 288
 		},
-		DEFAULT => -51,
+		DEFAULT => -52,
 		GOTOS => {
-			'else' => 307
+			'else' => 310
 		}
 	},
-	{#State 280
+	{#State 283
 		ACTIONS => {
-			'CASE' => 308
+			'CASE' => 311
 		},
-		DEFAULT => -56,
+		DEFAULT => -57,
 		GOTOS => {
-			'case' => 309
+			'case' => 312
 		}
 	},
-	{#State 281
+	{#State 284
 		ACTIONS => {
-			'END' => 310
+			'END' => 313
 		}
 	},
-	{#State 282
+	{#State 285
 		ACTIONS => {
-			'END' => 311
+			'END' => 314
 		}
 	},
-	{#State 283
+	{#State 286
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 312,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 315,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 284
+	{#State 287
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 313,
-			'directive' => 71,
+			'block' => 316,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 285
+	{#State 288
 		ACTIONS => {
-			";" => 314
+			";" => 317
 		}
 	},
-	{#State 286
+	{#State 289
 		ACTIONS => {
-			'END' => 315
+			'END' => 318
 		}
 	},
-	{#State 287
+	{#State 290
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 316,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 319,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 288
-		DEFAULT => -163
+	{#State 291
+		DEFAULT => -165
 	},
-	{#State 289
+	{#State 292
 		ACTIONS => {
-			'NOT' => 38,
-			"{" => 30,
-			'LITERAL' => 78,
+			'NOT' => 40,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"(" => 52,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'expr' => 317,
-			'sterm' => 68,
-			'item' => 39,
+			'expr' => 320,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 58,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 290
+	{#State 293
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 318,
-			'directive' => 71,
+			'block' => 321,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 291
-		DEFAULT => -68
+	{#State 294
+		DEFAULT => -69
 	},
-	{#State 292
+	{#State 295
 		ACTIONS => {
-			'DOT' => 173,
-			";" => 319
+			'DOT' => 176,
+			";" => 322
 		}
 	},
-	{#State 293
+	{#State 296
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 320,
-			'directive' => 71,
+			'block' => 323,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 294
+	{#State 297
 		ACTIONS => {
-			";" => 321
+			";" => 324
 		}
 	},
-	{#State 295
+	{#State 298
 		ACTIONS => {
-			"\"" => 322
+			"\"" => 325
 		}
 	},
-	{#State 296
-		DEFAULT => -81
+	{#State 299
+		DEFAULT => -83
 	},
-	{#State 297
+	{#State 300
 		ACTIONS => {
-			'END' => 323
+			'END' => 326
 		}
 	},
-	{#State 298
-		DEFAULT => -93
+	{#State 301
+		DEFAULT => -95
 	},
-	{#State 299
+	{#State 302
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
+			"(" => 54,
+			'SWITCH' => 55,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
 			'FOR' => 21,
 			'NEXT' => 22,
-			'LITERAL' => 57,
-			"\"" => 60,
-			'PROCESS' => 61,
+			'LITERAL' => 59,
+			"\"" => 62,
+			'PROCESS' => 63,
 			'FILTER' => 25,
-			'RETURN' => 64,
-			'INSERT' => 65,
+			'RETURN' => 66,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 191,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			"\${" => 37
+			'WHILE' => 69,
+			'BLOCK' => 194,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			"\${" => 38
 		},
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'expr' => 197,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'expr' => 200,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'atomdir' => 13,
-			'mdir' => 324,
-			'filter' => 29,
-			'sterm' => 68,
-			'ident' => 147,
-			'setlist' => 70,
-			'try' => 35,
-			'switch' => 34,
+			'mdir' => 327,
+			'filter' => 30,
+			'sterm' => 70,
+			'ident' => 150,
+			'setlist' => 72,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'directive' => 194,
-			'condition' => 73,
-			'lterm' => 56
+			'directive' => 197,
+			'condition' => 75,
+			'lterm' => 58
 		}
 	},
-	{#State 300
-		DEFAULT => -94
+	{#State 303
+		DEFAULT => -96
 	},
-	{#State 301
+	{#State 304
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -63,
+		DEFAULT => -64,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 302
+	{#State 305
 		ACTIONS => {
-			"{" => 30,
-			'COMMA' => 256,
-			'LITERAL' => 254,
+			'NOT' => 40,
+			"{" => 31,
+			'COMMA' => 259,
+			'LITERAL' => 257,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"(" => 54,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
-		DEFAULT => -64,
+		DEFAULT => -65,
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 252,
-			'param' => 253,
+			'expr' => 258,
+			'sterm' => 70,
+			'item' => 255,
+			'param' => 256,
 			'node' => 23,
-			'ident' => 251,
-			'term' => 255,
-			'lterm' => 56
+			'ident' => 254,
+			'term' => 60,
+			'lterm' => 58
 		}
 	},
-	{#State 303
+	{#State 306
 		ACTIONS => {
-			'END' => 325
+			'END' => 328
 		}
 	},
-	{#State 304
-		DEFAULT => -79
+	{#State 307
+		DEFAULT => -81
 	},
-	{#State 305
-		DEFAULT => -87
+	{#State 308
+		DEFAULT => -89
 	},
-	{#State 306
+	{#State 309
 		ACTIONS => {
-			'END' => 326
+			'END' => 329
 		}
 	},
-	{#State 307
+	{#State 310
 		ACTIONS => {
-			'END' => 327
+			'END' => 330
 		}
 	},
-	{#State 308
+	{#State 311
 		ACTIONS => {
-			";" => 328,
-			'DEFAULT' => 330,
-			"{" => 30,
-			'LITERAL' => 78,
+			";" => 331,
+			'DEFAULT' => 333,
+			"{" => 31,
+			'LITERAL' => 80,
 			'IDENT' => 2,
-			"\"" => 60,
-			"\$" => 42,
+			"\"" => 62,
+			"\$" => 44,
 			"[" => 10,
 			'NUMBER' => 26,
 			'REF' => 27,
-			"\${" => 37
+			"\${" => 38
 		},
 		GOTOS => {
-			'sterm' => 68,
-			'item' => 39,
+			'sterm' => 70,
+			'item' => 41,
 			'node' => 23,
-			'ident' => 77,
-			'term' => 329,
-			'lterm' => 56
+			'ident' => 79,
+			'term' => 332,
+			'lterm' => 58
 		}
 	},
-	{#State 309
+	{#State 312
 		ACTIONS => {
-			'END' => 331
+			'END' => 334
 		}
 	},
-	{#State 310
-		DEFAULT => -78
+	{#State 313
+		DEFAULT => -80
 	},
-	{#State 311
-		DEFAULT => -66
+	{#State 314
+		DEFAULT => -67
 	},
-	{#State 312
+	{#State 315
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			'DIV' => 161,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164,
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'MOD' => 167,
+			"/" => 168
 		},
-		DEFAULT => -142
+		DEFAULT => -144
 	},
-	{#State 313
+	{#State 316
 		ACTIONS => {
-			'END' => 332
+			'END' => 335
 		}
 	},
-	{#State 314
+	{#State 317
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 333,
-			'directive' => 71,
+			'block' => 336,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 315
-		DEFAULT => -47
+	{#State 318
+		DEFAULT => -48
 	},
-	{#State 316
+	{#State 319
 		ACTIONS => {
-			'CMPOP' => 163,
-			"?" => 157,
-			";" => 334,
-			"+" => 156,
-			'MOD' => 164,
-			'DIV' => 158,
-			"/" => 165,
-			'AND' => 159,
-			'CAT' => 162,
-			'BINOP' => 160,
-			'OR' => 161
+			'CMPOP' => 166,
+			"?" => 160,
+			";" => 337,
+			"+" => 159,
+			'MOD' => 167,
+			'DIV' => 161,
+			"/" => 168,
+			'AND' => 162,
+			'CAT' => 165,
+			'BINOP' => 163,
+			'OR' => 164
 		}
 	},
-	{#State 317
+	{#State 320
 		ACTIONS => {
-			"+" => 156,
-			'CAT' => 162,
-			'CMPOP' => 163,
-			"?" => 157,
-			'DIV' => 158,
-			'MOD' => 164,
-			"/" => 165,
-			'AND' => 159,
-			'BINOP' => 160,
-			'OR' => 161
+			"+" => 159,
+			'CAT' => 165,
+			'CMPOP' => 166,
+			"?" => 160,
+			'DIV' => 161,
+			'MOD' => 167,
+			"/" => 168,
+			'AND' => 162,
+			'BINOP' => 163,
+			'OR' => 164
 		},
-		DEFAULT => -153
+		DEFAULT => -155
 	},
-	{#State 318
-		DEFAULT => -72
+	{#State 321
+		DEFAULT => -73
 	},
-	{#State 319
+	{#State 322
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 335,
-			'directive' => 71,
+			'block' => 338,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 320
+	{#State 323
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 336
+			'final' => 339
 		}
 	},
-	{#State 321
+	{#State 324
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 337,
-			'directive' => 71,
+			'block' => 340,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 322
-		DEFAULT => -100
-	},
-	{#State 323
-		DEFAULT => -92
-	},
-	{#State 324
-		DEFAULT => -89
-	},
 	{#State 325
-		DEFAULT => -58
+		DEFAULT => -102
 	},
 	{#State 326
-		DEFAULT => -76
+		DEFAULT => -94
 	},
 	{#State 327
-		DEFAULT => -45
+		DEFAULT => -91
 	},
 	{#State 328
+		DEFAULT => -59
+	},
+	{#State 329
+		DEFAULT => -78
+	},
+	{#State 330
+		DEFAULT => -46
+	},
+	{#State 331
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 338,
-			'directive' => 71,
+			'block' => 341,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 329
+	{#State 332
 		ACTIONS => {
-			";" => 339
+			";" => 342
 		}
 	},
-	{#State 330
+	{#State 333
 		ACTIONS => {
-			";" => 340
+			";" => 343
 		}
 	},
-	{#State 331
-		DEFAULT => -52
+	{#State 334
+		DEFAULT => -53
 	},
-	{#State 332
-		DEFAULT => -61
+	{#State 335
+		DEFAULT => -62
 	},
-	{#State 333
-		DEFAULT => -50
+	{#State 336
+		DEFAULT => -51
 	},
-	{#State 334
+	{#State 337
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 341,
-			'directive' => 71,
+			'block' => 344,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 335
+	{#State 338
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 342
+			'final' => 345
 		}
 	},
-	{#State 336
-		DEFAULT => -71
+	{#State 339
+		DEFAULT => -72
 	},
-	{#State 337
+	{#State 340
 		ACTIONS => {
-			'FINAL' => 258,
-			'CATCH' => 260
+			'FINAL' => 261,
+			'CATCH' => 263
 		},
-		DEFAULT => -73,
+		DEFAULT => -74,
 		GOTOS => {
-			'final' => 343
+			'final' => 346
 		}
 	},
-	{#State 338
-		DEFAULT => -55
+	{#State 341
+		DEFAULT => -56
 	},
-	{#State 339
+	{#State 342
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 344,
-			'directive' => 71,
+			'block' => 347,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 340
+	{#State 343
 		ACTIONS => {
 			'SET' => 1,
-			'NOT' => 38,
+			'NOT' => 40,
 			'IDENT' => 2,
-			'CLEAR' => 40,
+			'CLEAR' => 42,
 			'UNLESS' => 4,
-			'IF' => 43,
-			"\$" => 42,
+			'IF' => 45,
+			"\$" => 44,
 			'STOP' => 7,
-			'CALL' => 44,
+			'CALL' => 46,
 			'THROW' => 9,
-			'GET' => 46,
+			'GET' => 48,
 			"[" => 10,
 			'TRY' => 11,
-			'LAST' => 48,
-			'DEBUG' => 50,
+			'LAST' => 50,
+			'DEBUG' => 52,
 			'META' => 15,
 			'INCLUDE' => 17,
-			"(" => 52,
-			'SWITCH' => 53,
+			"(" => 54,
+			'SWITCH' => 55,
 			'MACRO' => 18,
-			'JAVASCRIPT' => 54,
-			'WRAPPER' => 55,
-			";" => -18,
+			'JAVASCRIPT' => 56,
+			'WRAPPER' => 57,
+			";" => -19,
 			'FOR' => 21,
-			'LITERAL' => 57,
+			'LITERAL' => 59,
 			'NEXT' => 22,
-			'rawperl' => 59,
-			"\"" => 60,
+			'rawperl' => 61,
+			"\"" => 62,
 			'TEXT' => 24,
-			'PROCESS' => 61,
-			'RETURN' => 64,
+			'PROCESS' => 63,
+			'RETURN' => 66,
 			'FILTER' => 25,
-			'INSERT' => 65,
+			'INSERT' => 67,
 			'NUMBER' => 26,
 			'REF' => 27,
-			'WHILE' => 67,
-			'BLOCK' => 28,
-			'DEFAULT' => 69,
-			"{" => 30,
-			'perl' => 31,
-			'USE' => 32,
-			'VIEW' => 36,
-			"\${" => 37
+			'WHILE' => 69,
+			'RAW' => 28,
+			'BLOCK' => 29,
+			'DEFAULT' => 71,
+			"{" => 31,
+			'perl' => 32,
+			'USE' => 33,
+			'VIEW' => 37,
+			"\${" => 38
 		},
 		DEFAULT => -3,
 		GOTOS => {
-			'item' => 39,
+			'item' => 41,
 			'javascript' => 3,
 			'node' => 23,
-			'term' => 58,
+			'term' => 60,
 			'loop' => 5,
-			'use' => 63,
-			'expr' => 62,
-			'capture' => 41,
+			'use' => 65,
+			'expr' => 64,
+			'capture' => 43,
 			'statement' => 6,
 			'view' => 8,
-			'wrapper' => 45,
-			'atomexpr' => 47,
+			'wrapper' => 47,
+			'atomexpr' => 49,
 			'chunk' => 12,
-			'defblock' => 66,
+			'defblock' => 68,
 			'atomdir' => 13,
-			'anonblock' => 49,
-			'sterm' => 68,
+			'anonblock' => 51,
+			'sterm' => 70,
 			'defblockname' => 14,
-			'filter' => 29,
+			'filter' => 30,
 			'ident' => 16,
-			'setlist' => 70,
-			'chunks' => 33,
-			'try' => 35,
-			'switch' => 34,
+			'setlist' => 72,
+			'chunks' => 34,
+			'try' => 36,
+			'switch' => 35,
 			'assign' => 19,
-			'block' => 345,
-			'directive' => 71,
+			'block' => 348,
+			'directive' => 73,
 			'macro' => 20,
-			'condition' => 73,
-			'lterm' => 56
+			'condition' => 75,
+			'lterm' => 58,
+			'raw' => 39
 		}
 	},
-	{#State 341
+	{#State 344
 		ACTIONS => {
-			'ELSIF' => 287,
-			'ELSE' => 285
+			'ELSIF' => 290,
+			'ELSE' => 288
 		},
-		DEFAULT => -51,
+		DEFAULT => -52,
 		GOTOS => {
-			'else' => 346
+			'else' => 349
 		}
 	},
-	{#State 342
-		DEFAULT => -69
-	},
-	{#State 343
+	{#State 345
 		DEFAULT => -70
 	},
-	{#State 344
+	{#State 346
+		DEFAULT => -71
+	},
+	{#State 347
 		ACTIONS => {
-			'CASE' => 308
+			'CASE' => 311
 		},
-		DEFAULT => -56,
+		DEFAULT => -57,
 		GOTOS => {
-			'case' => 347
+			'case' => 350
 		}
 	},
-	{#State 345
-		DEFAULT => -54
+	{#State 348
+		DEFAULT => -55
 	},
-	{#State 346
-		DEFAULT => -49
+	{#State 349
+		DEFAULT => -50
 	},
-	{#State 347
-		DEFAULT => -53
+	{#State 350
+		DEFAULT => -54
 	}
 ]; 
 
@@ -5224,29 +5332,29 @@ sub
 		 'statement', 1, undef
 	],
 	[#Rule 16
+		 'statement', 1, undef
+	],
+	[#Rule 17
 		 'statement', 1,
 sub
-#line 94 "Parser.yp"
+#line 95 "Parser.yp"
 { $factory->get($_[1])                }
 	],
-	[#Rule 17
+	[#Rule 18
 		 'statement', 2,
 sub
-#line 95 "Parser.yp"
+#line 96 "Parser.yp"
 { $_[0]->add_metadata($_[2]);         }
 	],
-	[#Rule 18
+	[#Rule 19
 		 'statement', 0, undef
 	],
-	[#Rule 19
+	[#Rule 20
 		 'directive', 1,
 sub
-#line 99 "Parser.yp"
+#line 100 "Parser.yp"
 { $factory->set($_[1])                }
 	],
-	[#Rule 20
-		 'directive', 1, undef
-	],
 	[#Rule 21
 		 'directive', 1, undef
 	],
@@ -5266,96 +5374,99 @@ sub
 		 'directive', 1, undef
 	],
 	[#Rule 27
+		 'directive', 1, undef
+	],
+	[#Rule 28
 		 'atomexpr', 1,
 sub
-#line 114 "Parser.yp"
+#line 115 "Parser.yp"
 { $factory->get($_[1])                }
 	],
-	[#Rule 28
-		 'atomexpr', 1, undef
-	],
 	[#Rule 29
-		 'atomdir', 2,
-sub
-#line 118 "Parser.yp"
-{ $factory->get($_[2])                }
+		 'atomexpr', 1, undef
 	],
 	[#Rule 30
 		 'atomdir', 2,
 sub
 #line 119 "Parser.yp"
-{ $factory->call($_[2])               }
+{ $factory->get($_[2])                }
 	],
 	[#Rule 31
 		 'atomdir', 2,
 sub
 #line 120 "Parser.yp"
-{ $factory->set($_[2])                }
+{ $factory->call($_[2])               }
 	],
 	[#Rule 32
 		 'atomdir', 2,
 sub
 #line 121 "Parser.yp"
-{ $factory->default($_[2])            }
+{ $factory->set($_[2])                }
 	],
 	[#Rule 33
 		 'atomdir', 2,
 sub
 #line 122 "Parser.yp"
-{ $factory->insert($_[2])             }
+{ $factory->default($_[2])            }
 	],
 	[#Rule 34
 		 'atomdir', 2,
 sub
 #line 123 "Parser.yp"
-{ $factory->include($_[2])            }
+{ $factory->insert($_[2])             }
 	],
 	[#Rule 35
 		 'atomdir', 2,
 sub
 #line 124 "Parser.yp"
-{ $factory->process($_[2])            }
+{ $factory->include($_[2])            }
 	],
 	[#Rule 36
 		 'atomdir', 2,
 sub
 #line 125 "Parser.yp"
-{ $factory->throw($_[2])              }
+{ $factory->process($_[2])            }
 	],
 	[#Rule 37
-		 'atomdir', 1,
+		 'atomdir', 2,
 sub
 #line 126 "Parser.yp"
-{ $factory->return()                  }
+{ $factory->throw($_[2])              }
 	],
 	[#Rule 38
 		 'atomdir', 1,
 sub
 #line 127 "Parser.yp"
-{ $factory->stop()                    }
+{ $factory->return()                  }
 	],
 	[#Rule 39
 		 'atomdir', 1,
 sub
 #line 128 "Parser.yp"
-{ $factory->clear()                   }
+{ $factory->stop()                    }
 	],
 	[#Rule 40
 		 'atomdir', 1,
 sub
 #line 129 "Parser.yp"
-{ $factory->break()                   }
+{ $factory->clear()                   }
 	],
 	[#Rule 41
 		 'atomdir', 1,
 sub
 #line 130 "Parser.yp"
-{ $factory->next()                    }
+{ $factory->break()                   }
 	],
 	[#Rule 42
-		 'atomdir', 2,
+		 'atomdir', 1,
 sub
 #line 131 "Parser.yp"
+{ $factory->next()                    }
+	],
+	[#Rule 43
+		 'atomdir', 2,
+sub
+#line 132 "Parser.yp"
 { if ($_[2]->[0]->[0] =~ /^'(on|off)'$/) {
 				          $_[0]->{ DEBUG_DIRS } = ($1 eq 'on');
 					  $factory->debug($_[2]);
@@ -5365,761 +5476,767 @@ sub
 				      }
 				    }
 	],
-	[#Rule 43
-		 'atomdir', 1, undef
-	],
 	[#Rule 44
 		 'atomdir', 1, undef
 	],
 	[#Rule 45
+		 'atomdir', 1, undef
+	],
+	[#Rule 46
 		 'condition', 6,
 sub
-#line 144 "Parser.yp"
+#line 145 "Parser.yp"
 { $factory->if(@_[2, 4, 5])           }
 	],
-	[#Rule 46
+	[#Rule 47
 		 'condition', 3,
 sub
-#line 145 "Parser.yp"
+#line 146 "Parser.yp"
 { $factory->if(@_[3, 1])              }
 	],
-	[#Rule 47
+	[#Rule 48
 		 'condition', 6,
 sub
-#line 147 "Parser.yp"
+#line 148 "Parser.yp"
 { $factory->if("!($_[2])", @_[4, 5])  }
 	],
-	[#Rule 48
+	[#Rule 49
 		 'condition', 3,
 sub
-#line 148 "Parser.yp"
+#line 149 "Parser.yp"
 { $factory->if("!($_[3])", $_[1])     }
 	],
-	[#Rule 49
+	[#Rule 50
 		 'else', 5,
 sub
-#line 152 "Parser.yp"
+#line 153 "Parser.yp"
 { unshift(@{$_[5]}, [ @_[2, 4] ]);
 				      $_[5];                              }
 	],
-	[#Rule 50
+	[#Rule 51
 		 'else', 3,
 sub
-#line 154 "Parser.yp"
+#line 155 "Parser.yp"
 { [ $_[3] ]                           }
 	],
-	[#Rule 51
+	[#Rule 52
 		 'else', 0,
 sub
-#line 155 "Parser.yp"
+#line 156 "Parser.yp"
 { [ undef ]                           }
 	],
-	[#Rule 52
+	[#Rule 53
 		 'switch', 6,
 sub
-#line 159 "Parser.yp"
+#line 160 "Parser.yp"
 { $factory->switch(@_[2, 5])          }
 	],
-	[#Rule 53
+	[#Rule 54
 		 'case', 5,
 sub
-#line 163 "Parser.yp"
+#line 164 "Parser.yp"
 { unshift(@{$_[5]}, [ @_[2, 4] ]); 
 				      $_[5];                              }
 	],
-	[#Rule 54
+	[#Rule 55
 		 'case', 4,
 sub
-#line 165 "Parser.yp"
+#line 166 "Parser.yp"
 { [ $_[4] ]                           }
 	],
-	[#Rule 55
+	[#Rule 56
 		 'case', 3,
 sub
-#line 166 "Parser.yp"
+#line 167 "Parser.yp"
 { [ $_[3] ]                           }
 	],
-	[#Rule 56
+	[#Rule 57
 		 'case', 0,
 sub
-#line 167 "Parser.yp"
+#line 168 "Parser.yp"
 { [ undef ]                           }
 	],
-	[#Rule 57
+	[#Rule 58
 		 '@1-3', 0,
 sub
-#line 170 "Parser.yp"
+#line 171 "Parser.yp"
 { $_[0]->{ INFOR }++                  }
 	],
-	[#Rule 58
+	[#Rule 59
 		 'loop', 6,
 sub
-#line 171 "Parser.yp"
+#line 172 "Parser.yp"
 { $_[0]->{ INFOR }--;
 				      $factory->foreach(@{$_[2]}, $_[5])  }
 	],
-	[#Rule 59
+	[#Rule 60
 		 'loop', 3,
 sub
-#line 175 "Parser.yp"
+#line 176 "Parser.yp"
 { $factory->foreach(@{$_[3]}, $_[1])  }
 	],
-	[#Rule 60
+	[#Rule 61
 		 '@2-3', 0,
 sub
-#line 176 "Parser.yp"
+#line 177 "Parser.yp"
 { $_[0]->{ INWHILE }++                }
 	],
-	[#Rule 61
+	[#Rule 62
 		 'loop', 6,
 sub
-#line 177 "Parser.yp"
+#line 178 "Parser.yp"
 { $_[0]->{ INWHILE }--;
                                       $factory->while(@_[2, 5])           }
 	],
-	[#Rule 62
+	[#Rule 63
 		 'loop', 3,
 sub
-#line 179 "Parser.yp"
+#line 180 "Parser.yp"
 { $factory->while(@_[3, 1])           }
 	],
-	[#Rule 63
+	[#Rule 64
 		 'loopvar', 4,
 sub
-#line 182 "Parser.yp"
+#line 183 "Parser.yp"
 { [ @_[1, 3, 4] ]                     }
 	],
-	[#Rule 64
+	[#Rule 65
 		 'loopvar', 4,
 sub
-#line 183 "Parser.yp"
+#line 184 "Parser.yp"
 { [ @_[1, 3, 4] ]                     }
 	],
-	[#Rule 65
+	[#Rule 66
 		 'loopvar', 2,
 sub
-#line 184 "Parser.yp"
+#line 185 "Parser.yp"
 { [ 0, @_[1, 2] ]                     }
 	],
-	[#Rule 66
+	[#Rule 67
 		 'wrapper', 5,
 sub
-#line 188 "Parser.yp"
+#line 189 "Parser.yp"
 { $factory->wrapper(@_[2, 4])         }
 	],
-	[#Rule 67
+	[#Rule 68
 		 'wrapper', 3,
 sub
-#line 190 "Parser.yp"
+#line 191 "Parser.yp"
 { $factory->wrapper(@_[3, 1])         }
 	],
-	[#Rule 68
+	[#Rule 69
 		 'try', 5,
 sub
-#line 194 "Parser.yp"
+#line 195 "Parser.yp"
 { $factory->try(@_[3, 4])             }
 	],
-	[#Rule 69
+	[#Rule 70
 		 'final', 5,
 sub
-#line 198 "Parser.yp"
+#line 199 "Parser.yp"
 { unshift(@{$_[5]}, [ @_[2,4] ]);
 				      $_[5];                              }
 	],
-	[#Rule 70
+	[#Rule 71
 		 'final', 5,
 sub
-#line 201 "Parser.yp"
+#line 202 "Parser.yp"
 { unshift(@{$_[5]}, [ undef, $_[4] ]);
 				      $_[5];                              }
 	],
-	[#Rule 71
+	[#Rule 72
 		 'final', 4,
 sub
-#line 204 "Parser.yp"
+#line 205 "Parser.yp"
 { unshift(@{$_[4]}, [ undef, $_[3] ]);
 				      $_[4];                              }
 	],
-	[#Rule 72
+	[#Rule 73
 		 'final', 3,
 sub
-#line 206 "Parser.yp"
+#line 207 "Parser.yp"
 { [ $_[3] ]                           }
 	],
-	[#Rule 73
+	[#Rule 74
 		 'final', 0,
 sub
-#line 207 "Parser.yp"
+#line 208 "Parser.yp"
 { [ 0 ] }
 	],
-	[#Rule 74
+	[#Rule 75
 		 'use', 2,
 sub
-#line 210 "Parser.yp"
+#line 211 "Parser.yp"
 { $factory->use($_[2])                }
 	],
-	[#Rule 75
+	[#Rule 76
+		 'raw', 2,
+sub
+#line 214 "Parser.yp"
+{ $factory->raw($_[2])                }
+	],
+	[#Rule 77
 		 '@3-3', 0,
 sub
-#line 213 "Parser.yp"
+#line 217 "Parser.yp"
 { $_[0]->push_defblock();		  }
 	],
-	[#Rule 76
+	[#Rule 78
 		 'view', 6,
 sub
-#line 214 "Parser.yp"
+#line 218 "Parser.yp"
 { $factory->view(@_[2,5], 
 						     $_[0]->pop_defblock) }
 	],
-	[#Rule 77
+	[#Rule 79
 		 '@4-2', 0,
 sub
-#line 218 "Parser.yp"
+#line 222 "Parser.yp"
 { ${$_[0]->{ INJAVASCRIPT }}++;             }
 	],
-	[#Rule 78
+	[#Rule 80
 		 'javascript', 5,
 sub
-#line 219 "Parser.yp"
+#line 223 "Parser.yp"
 { ${$_[0]->{ INJAVASCRIPT }}--;
 				      $_[0]->{ EVAL_JAVASCRIPT } 
 				      ? $factory->javascript($_[4])             
 				      : $factory->no_javascript();              }
 	],
-	[#Rule 79
+	[#Rule 81
 		 'filter', 5,
 sub
-#line 226 "Parser.yp"
+#line 230 "Parser.yp"
 { $factory->filter(@_[2,4])           }
 	],
-	[#Rule 80
+	[#Rule 82
 		 'filter', 3,
 sub
-#line 228 "Parser.yp"
+#line 232 "Parser.yp"
 { $factory->filter(@_[3,1])           }
 	],
-	[#Rule 81
+	[#Rule 83
 		 'defblock', 5,
 sub
-#line 233 "Parser.yp"
+#line 237 "Parser.yp"
 { my $name = join('/', @{ $_[0]->{ DEFBLOCKS } });
 				      pop(@{ $_[0]->{ DEFBLOCKS } });
 				      $_[0]->define_block($name, $_[4]); 
 				      undef
 				    }
 	],
-	[#Rule 82
+	[#Rule 84
 		 'defblockname', 2,
 sub
-#line 240 "Parser.yp"
+#line 244 "Parser.yp"
 { push(@{ $_[0]->{ DEFBLOCKS } }, $_[2]);
 				      $_[2];
 				    }
 	],
-	[#Rule 83
+	[#Rule 85
 		 'blockname', 1, undef
 	],
-	[#Rule 84
+	[#Rule 86
 		 'blockname', 1,
 sub
-#line 246 "Parser.yp"
+#line 250 "Parser.yp"
 { $_[1] =~ s/^'(.*)'$/$1/; $_[1]      }
 	],
-	[#Rule 85
+	[#Rule 87
 		 'blockargs', 1, undef
 	],
-	[#Rule 86
+	[#Rule 88
 		 'blockargs', 0, undef
 	],
-	[#Rule 87
+	[#Rule 89
 		 'anonblock', 5,
 sub
-#line 254 "Parser.yp"
+#line 258 "Parser.yp"
 { local $" = ', ';
 				      print STDERR "experimental block args: [@{ $_[2] }]\n"
 					  if $_[2];
 				      $factory->anon_block($_[4])         }
 	],
-	[#Rule 88
+	[#Rule 90
 		 'capture', 3,
 sub
-#line 260 "Parser.yp"
+#line 264 "Parser.yp"
 { $factory->capture(@_[1, 3])         }
 	],
-	[#Rule 89
+	[#Rule 91
 		 'macro', 6,
 sub
-#line 264 "Parser.yp"
+#line 268 "Parser.yp"
 { $factory->macro(@_[2, 6, 4])        }
 	],
-	[#Rule 90
+	[#Rule 92
 		 'macro', 3,
 sub
-#line 265 "Parser.yp"
+#line 269 "Parser.yp"
 { $factory->macro(@_[2, 3])           }
 	],
-	[#Rule 91
+	[#Rule 93
 		 'mdir', 1, undef
 	],
-	[#Rule 92
+	[#Rule 94
 		 'mdir', 4,
 sub
-#line 269 "Parser.yp"
+#line 273 "Parser.yp"
 { $_[3]                               }
 	],
-	[#Rule 93
+	[#Rule 95
 		 'margs', 2,
 sub
-#line 272 "Parser.yp"
+#line 276 "Parser.yp"
 { push(@{$_[1]}, $_[2]); $_[1]        }
 	],
-	[#Rule 94
+	[#Rule 96
 		 'margs', 2,
 sub
-#line 273 "Parser.yp"
+#line 277 "Parser.yp"
 { $_[1]                               }
 	],
-	[#Rule 95
+	[#Rule 97
 		 'margs', 1,
 sub
-#line 274 "Parser.yp"
+#line 278 "Parser.yp"
 { [ $_[1] ]                           }
 	],
-	[#Rule 96
+	[#Rule 98
 		 'metadata', 2,
 sub
-#line 277 "Parser.yp"
+#line 281 "Parser.yp"
 { push(@{$_[1]}, @{$_[2]}); $_[1]     }
 	],
-	[#Rule 97
+	[#Rule 99
 		 'metadata', 2, undef
 	],
-	[#Rule 98
+	[#Rule 100
 		 'metadata', 1, undef
 	],
-	[#Rule 99
+	[#Rule 101
 		 'meta', 3,
 sub
-#line 282 "Parser.yp"
+#line 286 "Parser.yp"
 { for ($_[3]) { s/^'//; s/'$//; 
 						       s/\\'/'/g  }; 
 					 [ @_[1,3] ] }
 	],
-	[#Rule 100
+	[#Rule 102
 		 'meta', 5,
 sub
-#line 285 "Parser.yp"
+#line 289 "Parser.yp"
 { [ @_[1,4] ] }
 	],
-	[#Rule 101
+	[#Rule 103
 		 'meta', 3,
 sub
-#line 286 "Parser.yp"
+#line 290 "Parser.yp"
 { [ @_[1,3] ] }
 	],
-	[#Rule 102
+	[#Rule 104
 		 'term', 1, undef
 	],
-	[#Rule 103
+	[#Rule 105
 		 'term', 1, undef
 	],
-	[#Rule 104
+	[#Rule 106
 		 'lterm', 3,
 sub
-#line 298 "Parser.yp"
+#line 302 "Parser.yp"
 { "[ $_[2] ]"                         }
 	],
-	[#Rule 105
+	[#Rule 107
 		 'lterm', 3,
 sub
-#line 299 "Parser.yp"
+#line 303 "Parser.yp"
 { "[ $_[2] ]"                         }
 	],
-	[#Rule 106
+	[#Rule 108
 		 'lterm', 2,
 sub
-#line 300 "Parser.yp"
+#line 304 "Parser.yp"
 { "[ ]"                               }
 	],
-	[#Rule 107
+	[#Rule 109
 		 'lterm', 3,
 sub
-#line 301 "Parser.yp"
+#line 305 "Parser.yp"
 { "{ $_[2]  }"                        }
 	],
-	[#Rule 108
+	[#Rule 110
 		 'sterm', 1,
 sub
-#line 304 "Parser.yp"
+#line 308 "Parser.yp"
 { $factory->ident($_[1])              }
 	],
-	[#Rule 109
+	[#Rule 111
 		 'sterm', 2,
 sub
-#line 305 "Parser.yp"
+#line 309 "Parser.yp"
 { $factory->identref($_[2])           }
 	],
-	[#Rule 110
+	[#Rule 112
 		 'sterm', 3,
 sub
-#line 306 "Parser.yp"
+#line 310 "Parser.yp"
 { $factory->quoted($_[2])             }
 	],
-	[#Rule 111
+	[#Rule 113
 		 'sterm', 1, undef
 	],
-	[#Rule 112
+	[#Rule 114
 		 'sterm', 1, undef
 	],
-	[#Rule 113
+	[#Rule 115
 		 'list', 2,
 sub
-#line 311 "Parser.yp"
+#line 315 "Parser.yp"
 { "$_[1], $_[2]"                      }
 	],
-	[#Rule 114
+	[#Rule 116
 		 'list', 2, undef
 	],
-	[#Rule 115
+	[#Rule 117
 		 'list', 1, undef
 	],
-	[#Rule 116
+	[#Rule 118
 		 'range', 3,
 sub
-#line 316 "Parser.yp"
+#line 320 "Parser.yp"
 { $_[1] . '..' . $_[3]                }
 	],
-	[#Rule 117
+	[#Rule 119
 		 'hash', 1, undef
 	],
-	[#Rule 118
+	[#Rule 120
 		 'hash', 0,
 sub
-#line 321 "Parser.yp"
+#line 325 "Parser.yp"
 { "" }
 	],
-	[#Rule 119
+	[#Rule 121
 		 'params', 2,
 sub
-#line 324 "Parser.yp"
+#line 328 "Parser.yp"
 { "$_[1], $_[2]"                      }
 	],
-	[#Rule 120
+	[#Rule 122
 		 'params', 2, undef
 	],
-	[#Rule 121
+	[#Rule 123
 		 'params', 1, undef
 	],
-	[#Rule 122
+	[#Rule 124
 		 'param', 3,
 sub
-#line 329 "Parser.yp"
+#line 333 "Parser.yp"
 { "$_[1]: $_[3]"                    }
 	],
-	[#Rule 123
+	[#Rule 125
 		 'param', 3,
 sub
-#line 330 "Parser.yp"
+#line 334 "Parser.yp"
 { "$_[1]: $_[3]"                    }
 	],
-	[#Rule 124
+	[#Rule 126
 		 'ident', 3,
 sub
-#line 333 "Parser.yp"
+#line 337 "Parser.yp"
 { push(@{$_[1]}, @{$_[3]}); $_[1]     }
 	],
-	[#Rule 125
+	[#Rule 127
 		 'ident', 3,
 sub
-#line 334 "Parser.yp"
+#line 338 "Parser.yp"
 { push(@{$_[1]}, 
 					   map {($_, 0)} split(/\./, $_[3]));
 				      $_[1];			          }
 	],
-	[#Rule 126
+	[#Rule 128
 		 'ident', 1, undef
 	],
-	[#Rule 127
+	[#Rule 129
 		 'node', 1,
 sub
-#line 340 "Parser.yp"
+#line 344 "Parser.yp"
 { [ $_[1], 0 ]                        }
 	],
-	[#Rule 128
+	[#Rule 130
 		 'node', 4,
 sub
-#line 341 "Parser.yp"
+#line 345 "Parser.yp"
 { [ $_[1], $factory->args($_[3]) ]    }
 	],
-	[#Rule 129
+	[#Rule 131
 		 'item', 1,
 sub
-#line 344 "Parser.yp"
+#line 348 "Parser.yp"
 { "'$_[1]'"                           }
 	],
-	[#Rule 130
+	[#Rule 132
 		 'item', 3,
 sub
-#line 345 "Parser.yp"
+#line 349 "Parser.yp"
 { $_[2]                               }
 	],
-	[#Rule 131
+	[#Rule 133
 		 'item', 2,
 sub
-#line 346 "Parser.yp"
+#line 350 "Parser.yp"
 { $_[0]->{ V1DOLLAR }
 				       ? "'$_[2]'" 
 				       : $factory->ident(["'$_[2]'", 0])  }
 	],
-	[#Rule 132
+	[#Rule 134
 		 'expr', 3,
 sub
-#line 351 "Parser.yp"
+#line 355 "Parser.yp"
 { "$_[1] $_[2] $_[3]"                 }
 	],
-	[#Rule 133
+	[#Rule 135
 		 'expr', 3,
 sub
-#line 352 "Parser.yp"
+#line 356 "Parser.yp"
 { "$_[1] $_[2] $_[3]"                 }
 	],
-	[#Rule 134
+	[#Rule 136
 		 'expr', 3,
 sub
-#line 353 "Parser.yp"
+#line 357 "Parser.yp"
 { "$_[1] $_[2] $_[3]"                 }
 	],
-	[#Rule 135
+	[#Rule 137
 		 'expr', 3,
 sub
-#line 354 "Parser.yp"
-{ "int($_[1] / $_[3])"                }
+#line 358 "Parser.yp"
+{ "Math.floor($_[1] / $_[3])"                }
 	],
-	[#Rule 136
+	[#Rule 138
 		 'expr', 3,
 sub
-#line 355 "Parser.yp"
+#line 359 "Parser.yp"
 { "$_[1] % $_[3]"                     }
 	],
-	[#Rule 137
+	[#Rule 139
 		 'expr', 3,
 sub
-#line 356 "Parser.yp"
+#line 360 "Parser.yp"
 { "$_[1] $CMPOP{ $_[2] } $_[3]"       }
 	],
-	[#Rule 138
+	[#Rule 140
 		 'expr', 3,
 sub
-#line 357 "Parser.yp"
+#line 361 "Parser.yp"
 { "$_[1]  + $_[3]"                    }
 	],
-	[#Rule 139
+	[#Rule 141
 		 'expr', 3,
 sub
-#line 358 "Parser.yp"
+#line 362 "Parser.yp"
 { "$_[1] && $_[3]"                    }
 	],
-	[#Rule 140
+	[#Rule 142
 		 'expr', 3,
 sub
-#line 359 "Parser.yp"
+#line 363 "Parser.yp"
 { "$_[1] || $_[3]"                    }
 	],
-	[#Rule 141
+	[#Rule 143
 		 'expr', 2,
 sub
-#line 360 "Parser.yp"
+#line 364 "Parser.yp"
 { "! $_[2]"                           }
 	],
-	[#Rule 142
+	[#Rule 144
 		 'expr', 5,
 sub
-#line 361 "Parser.yp"
+#line 365 "Parser.yp"
 { "$_[1] ? $_[3] : $_[5]"             }
 	],
-	[#Rule 143
+	[#Rule 145
 		 'expr', 3,
 sub
-#line 362 "Parser.yp"
+#line 366 "Parser.yp"
 { $factory->assign(@{$_[2]})          }
 	],
-	[#Rule 144
+	[#Rule 146
 		 'expr', 3,
 sub
-#line 363 "Parser.yp"
+#line 367 "Parser.yp"
 { "($_[2])"                           }
 	],
-	[#Rule 145
+	[#Rule 147
 		 'expr', 1, undef
 	],
-	[#Rule 146
+	[#Rule 148
 		 'setlist', 2,
 sub
-#line 367 "Parser.yp"
+#line 371 "Parser.yp"
 { push(@{$_[1]}, @{$_[2]}); $_[1]     }
 	],
-	[#Rule 147
+	[#Rule 149
 		 'setlist', 2, undef
 	],
-	[#Rule 148
+	[#Rule 150
 		 'setlist', 1, undef
 	],
-	[#Rule 149
+	[#Rule 151
 		 'assign', 3,
 sub
-#line 373 "Parser.yp"
+#line 377 "Parser.yp"
 { [ $_[1], $_[3] ]                    }
 	],
-	[#Rule 150
+	[#Rule 152
 		 'assign', 3,
 sub
-#line 374 "Parser.yp"
+#line 378 "Parser.yp"
 { [ @_[1,3] ]                         }
 	],
-	[#Rule 151
+	[#Rule 153
 		 'args', 2,
 sub
-#line 381 "Parser.yp"
+#line 385 "Parser.yp"
 { push(@{$_[1]}, $_[2]); $_[1]        }
 	],
-	[#Rule 152
+	[#Rule 154
 		 'args', 2,
 sub
-#line 382 "Parser.yp"
+#line 386 "Parser.yp"
 { push(@{$_[1]->[0]}, $_[2]); $_[1]   }
 	],
-	[#Rule 153
+	[#Rule 155
 		 'args', 4,
 sub
-#line 383 "Parser.yp"
+#line 387 "Parser.yp"
 { push(@{$_[1]->[0]}, "'', " . 
 				      $factory->assign(@_[2,4])); $_[1]  }
 	],
-	[#Rule 154
+	[#Rule 156
 		 'args', 2,
 sub
-#line 385 "Parser.yp"
+#line 389 "Parser.yp"
 { $_[1]                               }
 	],
-	[#Rule 155
+	[#Rule 157
 		 'args', 0,
 sub
-#line 386 "Parser.yp"
+#line 390 "Parser.yp"
 { [ [ ] ]                             }
 	],
-	[#Rule 156
+	[#Rule 158
 		 'lnameargs', 3,
 sub
-#line 396 "Parser.yp"
+#line 400 "Parser.yp"
 { push(@{$_[3]}, $_[1]); $_[3]        }
 	],
-	[#Rule 157
+	[#Rule 159
 		 'lnameargs', 1, undef
 	],
-	[#Rule 158
+	[#Rule 160
 		 'lvalue', 1, undef
 	],
-	[#Rule 159
+	[#Rule 161
 		 'lvalue', 3,
 sub
-#line 401 "Parser.yp"
+#line 405 "Parser.yp"
 { $factory->quoted($_[2])             }
 	],
-	[#Rule 160
+	[#Rule 162
 		 'lvalue', 1, undef
 	],
-	[#Rule 161
+	[#Rule 163
 		 'nameargs', 3,
 sub
-#line 405 "Parser.yp"
+#line 409 "Parser.yp"
 { [ [$factory->ident($_[2])], $_[3] ]   }
 	],
-	[#Rule 162
+	[#Rule 164
 		 'nameargs', 2,
 sub
-#line 406 "Parser.yp"
+#line 410 "Parser.yp"
 { [ @_[1,2] ] }
 	],
-	[#Rule 163
+	[#Rule 165
 		 'nameargs', 4,
 sub
-#line 407 "Parser.yp"
+#line 411 "Parser.yp"
 { [ @_[1,3] ] }
 	],
-	[#Rule 164
+	[#Rule 166
 		 'names', 3,
 sub
-#line 410 "Parser.yp"
+#line 414 "Parser.yp"
 { push(@{$_[1]}, $_[3]); $_[1] }
 	],
-	[#Rule 165
+	[#Rule 167
 		 'names', 1,
 sub
-#line 411 "Parser.yp"
+#line 415 "Parser.yp"
 { [ $_[1] ]                    }
 	],
-	[#Rule 166
+	[#Rule 168
 		 'name', 3,
 sub
-#line 414 "Parser.yp"
+#line 418 "Parser.yp"
 { $factory->quoted($_[2])  }
 	],
-	[#Rule 167
+	[#Rule 169
 		 'name', 1,
 sub
-#line 415 "Parser.yp"
+#line 419 "Parser.yp"
 { "'$_[1]'" }
 	],
-	[#Rule 168
+	[#Rule 170
 		 'name', 1, undef
 	],
-	[#Rule 169
+	[#Rule 171
 		 'filename', 3,
 sub
-#line 427 "Parser.yp"
+#line 431 "Parser.yp"
 { "$_[1].$_[3]" }
 	],
-	[#Rule 170
+	[#Rule 172
 		 'filename', 1, undef
 	],
-	[#Rule 171
+	[#Rule 173
 		 'filepart', 1, undef
 	],
-	[#Rule 172
+	[#Rule 174
 		 'filepart', 1, undef
 	],
-	[#Rule 173
+	[#Rule 175
 		 'filepart', 1, undef
 	],
-	[#Rule 174
+	[#Rule 176
 		 'quoted', 2,
 sub
-#line 441 "Parser.yp"
+#line 445 "Parser.yp"
 { push(@{$_[1]}, $_[2]) 
 				          if defined $_[2]; $_[1]         }
 	],
-	[#Rule 175
+	[#Rule 177
 		 'quoted', 0,
 sub
-#line 443 "Parser.yp"
+#line 447 "Parser.yp"
 { [ ]                                 }
 	],
-	[#Rule 176
+	[#Rule 178
 		 'quotable', 1,
 sub
-#line 446 "Parser.yp"
+#line 450 "Parser.yp"
 { $factory->ident($_[1])              }
 	],
-	[#Rule 177
+	[#Rule 179
 		 'quotable', 1,
 sub
-#line 447 "Parser.yp"
+#line 451 "Parser.yp"
 { $factory->text($_[1])               }
 	],
-	[#Rule 178
+	[#Rule 180
 		 'quotable', 1,
 sub
-#line 448 "Parser.yp"
+#line 452 "Parser.yp"
 { undef                               }
 	]
 ];
@@ -5,7 +5,7 @@ use warnings;
 sub main { return &kernel }
 sub kernel {
     <<'...';
-if(typeof Jemplate=="undefined"){var Jemplate=function(){this.init.apply(this,arguments)}}Jemplate.VERSION="0.22";Jemplate.process=function(){var A=new Jemplate();return A.process.apply(A,arguments)};(function(){if(!Jemplate.templateMap){Jemplate.templateMap={}}var B=Jemplate.prototype={};B.init=function(C){this.config=C||{AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,FILTERS:{},INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]}};B.process=function(H,I,E){var G=this.config.CONTEXT||new Jemplate.Context();G.config=this.config;G.stash=this.config.STASH||new Jemplate.Stash();G.stash.__config__=this.config;G.__filter__=new Jemplate.Filter();G.__filter__.config=this.config;G.__plugin__=new Jemplate.Plugin();G.__plugin__.config=this.config;var C;var D=function(J){try{C=G.process(H,J)}catch(L){if(!String(L).match(/Jemplate\.STOP\n/)){throw (L)}C=L.toString().replace(/Jemplate\.STOP\n/,"")}if(typeof E=="undefined"){return C}if(typeof E=="function"){E(C);return null}if(typeof (E)=="string"||E instanceof String){if(E.match(/^#[\w\-]+$/)){var M=E.replace(/^#/,"");var K=document.getElementById(M);if(typeof K=="undefined"){throw ('No element found with id="'+M+'"')}K.innerHTML=C;return null}}else{E.innerHTML=C;return null}throw ("Invalid arguments in call to Jemplate.process");return 1};if(typeof I=="function"){I=I()}else{if(typeof I=="string"){var F=I;Jemplate.Ajax.processGet(F,function(J){D(J)});return null}}return D(I)};if(typeof Jemplate.Context=="undefined"){Jemplate.Context=function(){}}B=Jemplate.Context.prototype;B.include=function(D,C){return this.process(D,C,true)};B.process=function(E,D,G){if(G){this.stash.clone(D)}else{this.stash.update(D)}var F=Jemplate.templateMap[E];if(typeof F=="undefined"){throw ('No Jemplate template named "'+E+'" available')}var C=F(this);if(G){this.stash.declone()}return C};B.set_error=function(D,C){this._error=[D,C];return D};B.plugin=function(D,C){if(typeof D=="undefined"){throw"Unknown plugin name ':"+D+"'"}return new window[D](this,C)};B.filter=function(E,D,C){if(D=="null"){D="null_filter"}if(typeof this.__filter__.filters[D]=="function"){return this.__filter__.filters[D](E,C,this)}else{throw"Unknown filter name ':"+D+"'"}};if(typeof Jemplate.Plugin=="undefined"){Jemplate.Plugin=function(){}}B=Jemplate.Plugin.prototype;B.plugins={};if(typeof Jemplate.Filter=="undefined"){Jemplate.Filter=function(){}}B=Jemplate.Filter.prototype;B.filters={};B.filters.null_filter=function(C){return""};B.filters.upper=function(C){return C.toUpperCase()};B.filters.lower=function(C){return C.toLowerCase()};B.filters.ucfirst=function(E){var D=E.charAt(0);var C=E.substr(1);return D.toUpperCase()+C};B.filters.lcfirst=function(E){var D=E.charAt(0);var C=E.substr(1);return D.toLowerCase()+C};B.filters.trim=function(C){return C.replace(/^\s+/g,"").replace(/\s+$/g,"")};B.filters.collapse=function(C){return C.replace(/^\s+/g,"").replace(/\s+$/g,"").replace(/\s+/," ")};B.filters.html=function(C){C=C.replace(/&/g,"&amp;");C=C.replace(/</g,"&lt;");C=C.replace(/>/g,"&gt;");C=C.replace(/"/g,"&quot;");return C};B.filters.html_para=function(D){var C=D.split(/(?:\r?\n){2,}/);return"<p>\n"+C.join("\n</p>\n\n<p>\n")+"</p>\n"};B.filters.html_break=function(C){return C.replace(/(\r?\n){2,}/g,"$1<br />$1<br />$1")};B.filters.html_line_break=function(C){return C.replace(/(\r?\n)/g,"$1<br />$1")};B.filters.uri=function(C){return encodeURIComponent(C)};B.filters.url=function(C){return encodeURI(C)};B.filters.indent=function(H,D){var F=D[0];if(!H){return null}if(typeof F=="undefined"){F=4}var G="";if(typeof F=="number"||String(F).match(/^\d$/)){for(var E=0;E<F;E++){G+=" "}}else{G=F}var C=H.replace(/^/gm,G);return C};B.filters.truncate=function(F,D){var C=D[0];if(!F){return null}if(!C){C=32}if(F.length<C){return F}var E=C-3;return F.substr(0,E)+"..."};B.filters.repeat=function(F,C){if(!F){return null}if(!C||C==0){C=1}if(C==1){return F}var D=F;for(var E=1;E<C;E++){D+=F}return D};B.filters.replace=function(G,D){if(!G){return null}var C=D[0];var F=D[1];if(!C){C=""}if(!F){F=""}var E=new RegExp(C,"g");return G.replace(E,F)};if(typeof Jemplate.Stash=="undefined"){Jemplate.Stash=function(){this.data={}}}B=Jemplate.Stash.prototype;B.clone=function(C){var D=this.data;this.data={};this.update(D);this.update(C);this.data._PARENT=D};B.declone=function(C){this.data=this.data._PARENT||this.data};B.update=function(C){if(typeof C=="undefined"){return }for(var D in C){var E=C[D];this.set(D,E)}};B.get=function(F){var C=this.data;if(F instanceof Array){for(var E=0;E<F.length;E+=2){var D=F.slice(E,E+2);D.unshift(C);value=this._dotop.apply(this,D);if(typeof value=="undefined"){break}C=value}}else{value=this._dotop(C,F)}if(typeof value=="undefined"){if(this.__config__.DEBUG_UNDEF){throw ("undefined value found while using DEGUG_UNDEF")}value=""}return value};B.set=function(D,F,C){if(D instanceof Array){var E=this.get(D[0])||{};D=D[2]}else{E=this.data}if(!(C&&(typeof E[D]!="undefined"))){E[D]=F}};B._dotop=function(C,E,D){if(typeof E=="undefined"||typeof E=="string"&&E.match(/^[\._]/)){return undefined}if((!D)&&(typeof C=="object")&&(!(C instanceof Array)||(typeof E=="number"))&&(typeof C[E]!="undefined")){var F=C[E];if(typeof F=="function"){F=F.apply(C)}return F}if(typeof C=="string"&&this.string_functions[E]){return this.string_functions[E](C,D)}if(C instanceof Array&&this.list_functions[E]){return this.list_functions[E](C,D)}if(typeof C=="object"&&this.hash_functions[E]){return this.hash_functions[E](C,D)}if(typeof C[E]=="function"){return C[E].apply(C,D)}return undefined};B.string_functions={};B.string_functions.chunk=function(D,C){var E=C[0];var F=new Array();if(!E){E=1}if(E<0){E=0-E;for(i=D.length-E;i>=0;i=i-E){F.unshift(D.substr(i,E))}if(D.length%E){F.unshift(D.substr(0,D.length%E))}}else{for(i=0;i<D.length;i=i+E){F.push(D.substr(i,E))}}return F};B.string_functions.defined=function(C){return 1};B.string_functions.hash=function(C){return{"value":C}};B.string_functions.length=function(C){return C.length};B.string_functions.list=function(C){return[C]};B.string_functions.match=function(D,C){var F=new RegExp(C[0],"gm");var E=D.match(F);return E};B.string_functions.repeat=function(E,D){var G=D[0]||1;var C="";for(var F=0;F<G;F++){C+=E}return C};B.string_functions.replace=function(E,D){var G=new RegExp(D[0],"gm");var F=D[1];if(!F){F=""}var C=E.replace(G,F);return C};B.string_functions.search=function(D,C){var E=new RegExp(C[0]);return(D.search(E)>=0)?1:0};B.string_functions.size=function(C){return 1};B.string_functions.split=function(D,C){var F=new RegExp(C[0]);var E=D.split(F);return E};B.list_functions={};B.list_functions.join=function(D,C){return D.join(C[0])};B.list_functions.sort=function(D,C){if(typeof (C)!="undefined"&&C!=""){return D.sort(function(F,E){if(F[C]==E[C]){return 0}else{if(F[C]>E[C]){return 1}else{return -1}}})}return D.sort()};B.list_functions.nsort=function(C){return C.sort(function(E,D){return(E-D)})};B.list_functions.grep=function(G,D){var F=new RegExp(D[0]);var C=[];for(var E=0;E<G.length;E++){if(G[E].match(F)){C.push(G[E])}}return C};B.list_functions.unique=function(G){var C=[];var D={};for(var E=0;E<G.length;E++){var F=G[E];if(!D[F]){C.push(F)}D[F]=true}return C};B.list_functions.reverse=function(E){var C=[];for(var D=E.length-1;D>=0;D--){C.push(E[D])}return C};B.list_functions.merge=function(G,E){var C=[];var D=function(I){if(I instanceof Array){for(var H=0;H<I.length;H++){C.push(I[H])}}else{C.push(I)}};D(G);for(var F=0;F<E.length;F++){D(E[F])}return C};B.list_functions.slice=function(D,C){return D.slice(C[0],C[1])};B.list_functions.splice=function(D,C){if(C.length==1){return D.splice(C[0])}if(C.length==2){return D.splice(C[0],C[1])}if(C.length==3){return D.splice(C[0],C[1],C[2])}return null};B.list_functions.push=function(D,C){D.push(C[0]);return D};B.list_functions.pop=function(C){return C.pop()};B.list_functions.unshift=function(D,C){D.unshift(C[0]);return D};B.list_functions.shift=function(C){return C.shift()};B.list_functions.first=function(C){return C[0]};B.list_functions.size=function(C){return C.length};B.list_functions.max=function(C){return C.length-1};B.list_functions.last=function(C){return C.slice(-1)};B.hash_functions={};B.hash_functions.each=function(E){var D=new Array();for(var C in E){D.push(C,E[C])}return D};B.hash_functions.exists=function(D,C){return(typeof (D[C[0]])=="undefined")?0:1};B.hash_functions.keys=function(E){var D=new Array();for(var C in E){D.push(C)}return D};B.hash_functions.list=function(G,C){var F="";if(C){F=C[0]}var E=new Array();var D;if(F=="keys"){for(D in G){E.push(D)}}else{if(F=="values"){for(D in G){E.push(G[D])}}else{if(F=="each"){for(D in G){E.push(D,G[D])}}else{for(D in G){E.push({"key":D,"value":G[D]})}}}}return E};B.hash_functions.nsort=function(E){var D=new Array();for(var C in E){D.push(C)}return D.sort(function(G,F){return(G-F)})};B.hash_functions.size=function(E){var D=0;for(var C in E){D++}return D};B.hash_functions.sort=function(E){var D=new Array();for(var C in E){D.push(C)}return D.sort()};B.hash_functions.values=function(E){var D=new Array();for(var C in E){D.push(E[C])}return D};B.hash_functions.remove=function(D,C){return delete D[C[0]]};B.hash_functions["delete"]=B.hash_functions.remove;if(typeof Jemplate.Iterator=="undefined"){Jemplate.Iterator=function(D){if(D instanceof Array){this.object=D;this.size=D.length;this.max=this.size-1}else{if(D instanceof Object){this.object=D;var C=new Array;for(var E in D){C[C.length]=E}this.object_keys=C.sort();this.size=C.length;this.max=this.size-1}}}}B=Jemplate.Iterator.prototype;B.get_first=function(){this.index=0;this.first=1;this.last=0;this.count=1;return this.get_next(1)};B.get_next=function(C){var E=this.object;var D;if(typeof (C)!="undefined"&&C){D=this.index}else{D=++this.index;this.first=0;this.count=this.index+1;if(this.index==this.size-1){this.last=1}}if(typeof E=="undefined"){throw ("No object to iterate")}if(this.object_keys){if(D<this.object_keys.length){this.prev=D>0?this.object_keys[D-1]:"";this.next=D<this.max?this.object_keys[D+1]:"";return[this.object_keys[D],false]}}else{if(D<E.length){this.prev=D>0?E[D-1]:"";this.next=D<this.max?E[D+1]:"";return[E[D],false]}}return[null,true]};var A="stub that doesn't do anything. Try including the jQuery, YUI, or XHR option when building the runtime";Jemplate.Ajax={get:function(C,D){throw ("This is a Jemplate.Ajax.get "+A)},processGet:function(C,D){throw ("This is a Jemplate.Ajax.processGet "+A)},post:function(C,D){throw ("This is a Jemplate.Ajax.post "+A)}};Jemplate.JSON={parse:function(C){throw ("This is a Jemplate.JSON.parse "+A)},stringify:function(C){throw ("This is a Jemplate.JSON.stringify "+A)}}}())
+if(typeof Jemplate=="undefined"){var Jemplate=function(){this.init.apply(this,arguments)}}Jemplate.VERSION="0.22";Jemplate.process=function(){var A=new Jemplate(Jemplate.prototype.config);return A.process.apply(A,arguments)};(function(){if(!Jemplate.templateMap){Jemplate.templateMap={}}var proto=Jemplate.prototype={};proto.config={AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,GLOBAL:true,SCOPE:this,FILTERS:{},INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]};proto.defaults={AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,GLOBAL:true,SCOPE:this,INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]};Jemplate.init=function(config){Jemplate.prototype.config=config||{};for(var i in Jemplate.prototype.defaults){if(typeof Jemplate.prototype.config[i]=="undefined"){Jemplate.prototype.config[i]=Jemplate.prototype.defaults[i]}}};proto.init=function(config){this.config=config||{};for(var i in Jemplate.prototype.defaults){if(typeof this.config[i]=="undefined"){this.config[i]=Jemplate.prototype.defaults[i]}}};proto.process=function(template,data,output){var context=this.config.CONTEXT||new Jemplate.Context();context.config=this.config;context.stash=new Jemplate.Stash(this.config.STASH,this.config);context.__filter__=new Jemplate.Filter();context.__filter__.config=this.config;context.__plugin__=new Jemplate.Plugin();context.__plugin__.config=this.config;var result;var proc=function(input){try{if(typeof context.config.PRE_PROCESS=="string"){context.config.PRE_PROCESS=[context.config.PRE_PROCESS]}for(var i=0;i<context.config.PRE_PROCESS.length;i++){context.process(context.config.PRE_PROCESS[i])}result=context.process(template,input);if(typeof context.config.POST_PROCESS=="string"){context.config.PRE_PROCESS=[context.config.POST_PROCESS]}for(i=0;i<context.config.POST_PROCESS.length;i++){context.process(context.config.POST_PROCESS[i])}}catch(e){if(!String(e).match(/Jemplate\.STOP\n/)){throw (e)}result=e.toString().replace(/Jemplate\.STOP\n/,"")}if(typeof output=="undefined"){return result}if(typeof output=="function"){output(result);return null}if(typeof (output)=="string"||output instanceof String){if(output.match(/^#[\w\-]+$/)){var id=output.replace(/^#/,"");var element=document.getElementById(id);if(typeof element=="undefined"){throw ('No element found with id="'+id+'"')}element.innerHTML=result;return null}}else{output.innerHTML=result;return null}throw ("Invalid arguments in call to Jemplate.process");return 1};if(typeof data=="function"){data=data()}else{if(typeof data=="string"){var url=data;Jemplate.Ajax.processGet(url,function(data){proc(data)});return null}}return proc(data)};if(typeof Jemplate.Context=="undefined"){Jemplate.Context=function(){}}proto=Jemplate.Context.prototype;proto.include=function(template,args){return this.process(template,args,true)};proto.process=function(template,args,localise){if(localise){this.stash.clone(args)}else{this.stash.update(args)}var func=Jemplate.templateMap[template];if(typeof func=="undefined"){throw ('No Jemplate template named "'+template+'" available')}var output=func(this);if(localise){this.stash.declone()}return output};proto.set_error=function(error,output){this._error=[error,output];return error};proto.plugin=function(name,args){if(typeof name=="undefined"){throw"Unknown plugin name ':"+name+"'"}var func=eval(name);return new func(this,args)};proto.filter=function(text,name,args){if(name=="null"){name="null_filter"}if(typeof this.__filter__.filters[name]=="function"){return this.__filter__.filters[name](text,args,this)}else{throw"Unknown filter name ':"+name+"'"}};if(typeof Jemplate.Plugin=="undefined"){Jemplate.Plugin=function(){}}proto=Jemplate.Plugin.prototype;proto.plugins={};if(typeof Jemplate.Filter=="undefined"){Jemplate.Filter=function(){}}proto=Jemplate.Filter.prototype;proto.filters={};proto.filters.null_filter=function(text){return""};proto.filters.upper=function(text){return text.toUpperCase()};proto.filters.lower=function(text){return text.toLowerCase()};proto.filters.ucfirst=function(text){var first=text.charAt(0);var rest=text.substr(1);return first.toUpperCase()+rest};proto.filters.lcfirst=function(text){var first=text.charAt(0);var rest=text.substr(1);return first.toLowerCase()+rest};proto.filters.trim=function(text){return text.replace(/^\s+/g,"").replace(/\s+$/g,"")};proto.filters.collapse=function(text){return text.replace(/^\s+/g,"").replace(/\s+$/g,"").replace(/\s+/," ")};proto.filters.html=function(text){text=text.replace(/&/g,"&amp;");text=text.replace(/</g,"&lt;");text=text.replace(/>/g,"&gt;");text=text.replace(/"/g,"&quot;");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"<p>\n"+lines.join("\n</p>\n\n<p>\n")+"</p>\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1<br />$1<br />$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1<br />$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i<pad;i++){finalpad+=" "}}else{finalpad=pad}var output=text.replace(/^/gm,finalpad);return output};proto.filters.truncate=function(text,args){var len=args[0];if(!text){return null}if(!len){len=32}if(text.length<len){return text}var newlen=len-3;return text.substr(0,newlen)+"..."};proto.filters.repeat=function(text,iter){if(!text){return null}if(!iter||iter==0){iter=1}if(iter==1){return text}var output=text;for(var i=1;i<iter;i++){output+=text}return output};proto.filters.replace=function(text,args){if(!text){return null}var re_search=args[0];var text_replace=args[1];if(!re_search){re_search=""}if(!text_replace){text_replace=""}var re=new RegExp(re_search,"g");return text.replace(re,text_replace)};if(typeof Jemplate.Stash=="undefined"){Jemplate.Stash=function(stash,config){this.__config__=config;this.data={GLOBAL:this.__config__.SCOPE};this.LOCAL_ANCHOR={};this.data.LOCAL=this.LOCAL_ANCHOR;this.update(stash)}}proto=Jemplate.Stash.prototype;proto.clone=function(args){var data=this.data;this.data={GLOBAL:this.__config__.SCOPE};this.data.LOCAL=this.LOCAL_ANCHOR;this.update(data);this.update(args);this.data._PARENT=data};proto.declone=function(args){this.data=this.data._PARENT||this.data};proto.update=function(args){if(typeof args=="undefined"){return }for(var key in args){if(key!="GLOBAL"&&key!="LOCAL"){this.set(key,args[key])}}};proto.get=function(ident,args){var root=this.data;var value;if((ident instanceof Array)||(typeof ident=="string"&&/\./.test(ident))){if(typeof ident=="string"){ident=ident.split(".");var newIdent=[];for(var i=0;i<ident.length;i++){newIdent.push(ident.replace(/\(.*$/,""));newIdent.push(0)}ident=newIdent}for(var i=0;i<ident.length;i+=2){var dotopArgs=ident.slice(i,i+2);dotopArgs.unshift(root);value=this._dotop.apply(this,dotopArgs);if(typeof value=="undefined"){break}root=value}}else{value=this._dotop(root,ident,args)}if(typeof value=="undefined"||value==null){if(this.__config__.DEBUG_UNDEF){throw ("undefined value found while using DEBUG_UNDEF")}value=""}return value};proto.set=function(ident,value,set_default){var root,result,error;root=this.data;while(true){if((ident instanceof Array)||(typeof ident=="string"&&/\./.test(ident))){if(typeof ident=="string"){ident=ident.split(".");var newIdent=[];for(var i=0;i<ident.length;i++){newIdent.push(ident.replace(/\(.*$/,""));newIdent.push(0)}ident=newIdent}for(var i=0;i<ident.length-2;i+=2){var dotopArgs=ident.slice(i,i+2);dotopArgs.unshift(root);dotopArgs.push(1);result=this._dotop.apply(this,dotopArgs);if(typeof value=="undefined"){break}root=result}var assignArgs=ident.slice(ident.length-2,ident.length);assignArgs.unshift(root);assignArgs.push(value);assignArgs.push(set_default);result=this._assign.apply(this,assignArgs)}else{result=this._assign(root,ident,0,value,set_default)}break}return(typeof result!="undefined")?result:""};proto._dotop=function(root,item,args,lvalue){if(root==this.LOCAL_ANCHOR){root=this.data}var atroot=root==this.data;var value,result=undefined;var is_function_call=args instanceof Array;args=args||[];if(typeof root=="undefined"||typeof item=="undefined"||typeof item=="string"&&item.match(/^[\._]/)){return undefined}if(atroot||(root instanceof Object&&!(root instanceof Array))||root==this.data.GLOBAL){if(typeof root[item]!="undefined"&&root[item]!=null&&(!is_function_call||!this.hash_functions[item])){if(typeof root[item]=="function"){result=root[item].apply(root,args)}else{return root[item]}}else{if(lvalue){return root[item]={}}else{if(this.hash_functions[item]&&!atroot||item=="import"){args.unshift(root);result=this.hash_functions[item].apply(this,args)}else{if(item instanceof Array){result={};for(var i=0;i<item.length;i++){result[item[i]]=root[item[i]]}return result}}}}}else{if(root instanceof Array){if(this.list_functions[item]){args.unshift(root);result=this.list_functions[item].apply(this,args)}else{if(typeof item=="string"&&/^-?\d+$/.test(item)||typeof item=="number"){if(typeof root[item]!="function"){return root[item]}result=root[item].apply(this,args)}else{if(item instanceof Array){for(var i=0;i<item.length;i++){result.push(root[item[i]])}return result}}}}else{if(this.string_functions[item]&&!lvalue){args.unshift(root);result=this.string_functions[item].apply(this,args)}else{if(this.list_functions[item]&&!lvalue){args.unshift([root]);result=this.list_functions[item].apply(this,args)}else{result=undefined}}}}if(result instanceof Array){if(typeof result[0]=="undefined"&&typeof result[1]!="undefined"){throw result[1]}}return result};proto._assign=function(root,item,args,value,set_default){var atroot=root==this.data;var result;args=args||[];if(typeof root=="undefined"||typeof item=="undefined"||typeof item=="string"&&item.match(/^[\._]/)){return undefined}if(atroot||root.constructor==Object||root==this.data.GLOBAL){if(root==this.LOCAL_ANCHOR){root=this.data}if(!(set_default&&typeof root[item]!="undefined")){if(atroot&&item=="GLOBAL"){throw"Attempt to modify GLOBAL access modifier"}if(atroot&&item=="LOCAL"){throw"Attempt to modify LOCAL access modifier"}return root[item]=value}}else{if((root instanceof Array)&&(typeof item=="string"&&/^-?\d+$/.test(item)||typeof item=="number")){if(!(set_default&&typeof root[item]!="undefined")){return root[item]=value}}else{if((root.constructor!=Object)&&(root instanceof Object)){try{result=root[item].apply(root,args)}catch(e){}}else{throw"dont know how to assign to ["+root+"."+item+"]"}}}return undefined};proto.string_functions={};proto.string_functions["typeof"]=function(value){return typeof value};proto.string_functions.chunk=function(string,size){var list=new Array();if(!size){size=1}if(size<0){size=0-size;for(var i=string.length-size;i>=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i<string.length;i=i+size){list.push(string.substr(i,size))}}return list};proto.string_functions.defined=function(string){return 1};proto.string_functions.hash=function(string){return{"value":string}};proto.string_functions.length=function(string){return string.length};proto.string_functions.list=function(string){return[string]};proto.string_functions.match=function(string,re,modifiers){var regexp=new RegExp(re,modifiers==undefined?"g":modifiers);var list=string.match(regexp);return list};proto.string_functions.repeat=function(string,args){var n=args||1;var output="";for(var i=0;i<n;i++){output+=string}return output};proto.string_functions.replace=function(string,re,sub,modifiers){var regexp=new RegExp(re,modifiers==undefined?"g":modifiers);if(!sub){sub=""}return string.replace(regexp,sub)};proto.string_functions.search=function(string,re){var regexp=new RegExp(re);return(string.search(regexp)>=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i<list.length;i++){if(list[i].match(regexp)){result.push(list[i])}}return result};proto.list_functions.unique=function(list){var result=[];var seen={};for(var i=0;i<list.length;i++){var elem=list[i];if(!seen[elem]){result.push(elem)}seen[elem]=true}return result};proto.list_functions.reverse=function(list){var result=[];for(var i=list.length-1;i>=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j<elem.length;j++){result.push(elem[j])}}else{result.push(elem)}};push_all(list);for(var i=1;i<arguments.length;i++){push_all(arguments[i])}return result};proto.list_functions.slice=function(list,start,end){if(end==-1){return list.slice(start)}return list.slice(start,end+1)};proto.list_functions.splice=function(list){var args=Array.prototype.slice.call(arguments);args.shift();return list.splice.apply(list,args)};proto.list_functions.push=function(list,value){list.push(value);return list};proto.list_functions.pop=function(list){return list.pop()};proto.list_functions.unshift=function(list,value){list.unshift(value);return list};proto.list_functions.shift=function(list){return list.shift()};proto.list_functions.first=function(list){return list[0]};proto.list_functions.size=function(list){return list.length};proto.list_functions.max=function(list){return list.length-1};proto.list_functions.last=function(list){return list.slice(-1)};proto.hash_functions={};proto.hash_functions["typeof"]=function(hash){return"object"};proto.hash_functions.each=function(hash){var list=new Array();for(var key in hash){list.push(key,hash[key])}return list};proto.hash_functions.exists=function(hash,key){return(typeof (hash[key])=="undefined")?0:1};proto.hash_functions["import"]=function(hash,hash2){for(var key in hash2){hash[key]=hash2[key]}return""};proto.hash_functions.keys=function(hash){var list=new Array();for(var key in hash){list.push(key)}return list};proto.hash_functions.list=function(hash,what){var list=new Array();var key;if(what=="keys"){for(key in hash){list.push(key)}}else{if(what=="values"){for(key in hash){list.push(hash[key])}}else{if(what=="each"){for(key in hash){list.push(key,hash[key])}}else{for(key in hash){list.push({"key":key,"value":hash[key]})}}}}return list};proto.hash_functions.nsort=function(hash){var list=new Array();for(var key in hash){list.push(key)}return list.sort(function(a,b){return(a-b)})};proto.hash_functions.item=function(hash,key){return hash[key]};proto.hash_functions.size=function(hash){var size=0;for(var key in hash){size++}return size};proto.hash_functions.sort=function(hash){var list=new Array();for(var key in hash){list.push(key)}return list.sort()};proto.hash_functions.values=function(hash){var list=new Array();for(var key in hash){list.push(hash[key])}return list};proto.hash_functions.pairs=function(hash){var list=new Array();var keys=new Array();for(var key in hash){keys.push(key)}keys.sort();for(var key in keys){key=keys[key];list.push({"key":key,"value":hash[key]})}return list};proto.hash_functions.remove=function(hash,key){return delete hash[key]};proto.hash_functions["delete"]=proto.hash_functions.remove;if(typeof Jemplate.Iterator=="undefined"){Jemplate.Iterator=function(object){if(object instanceof Array){this.object=object;this.size=object.length;this.max=this.size-1}else{if(object instanceof Object){this.object=object;var object_keys=new Array;for(var key in object){object_keys[object_keys.length]=key}this.object_keys=object_keys.sort();this.size=object_keys.length;this.max=this.size-1}else{if(typeof object=="undefined"||object==null||object==""){this.object=null;this.max=-1}}}}}proto=Jemplate.Iterator.prototype;proto.get_first=function(){this.index=0;this.first=1;this.last=0;this.count=1;return this.get_next(1)};proto.get_next=function(should_init){var object=this.object;var index;if(typeof (should_init)!="undefined"&&should_init){index=this.index}else{index=++this.index;this.first=0;this.count=this.index+1;if(this.index==this.size-1){this.last=1}}if(typeof object=="undefined"){throw ("No object to iterate")}if(this.object_keys){if(index<this.object_keys.length){this.prev=index>0?this.object_keys[index-1]:"";this.next=index<this.max?this.object_keys[index+1]:"";return[this.object_keys[index],false]}}else{if(index<=this.max){this.prev=index>0?object[index-1]:"";this.next=index<this.max?object[index+1]:"";return[object[index],false]}}return[null,true]};var stubExplanation="stub that doesn't do anything. Try including the jQuery, YUI, or XHR option when building the runtime";Jemplate.Ajax={get:function(url,callback){throw ("This is a Jemplate.Ajax.get "+stubExplanation)},processGet:function(url,callback){throw ("This is a Jemplate.Ajax.processGet "+stubExplanation)},post:function(url,callback){throw ("This is a Jemplate.Ajax.post "+stubExplanation)}};Jemplate.JSON={parse:function(decodeValue){throw ("This is a Jemplate.JSON.parse "+stubExplanation)},stringify:function(encodeValue){throw ("This is a Jemplate.JSON.stringify "+stubExplanation)}}}())
 ...
 }
 
@@ -39,7 +39,7 @@ sub json_json2_internal {
 
 var JSON;
 
-if(!JSON){var JSON}if(!JSON){JSON=function(){function f(n){return n<10?"0"+n:n}Date.prototype.toJSON=function(){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){return escapeable.test(string)?'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==="string"){return c}c=a.charCodeAt();return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(typeof value.length==="number"&&!(value.propertyIsEnumerable("length"))){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return null}return{stringify:function(value,replacer,space){var i;gap="";indent="";if(space){if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}}if(!replacer){rep=function(key,value){if(!Object.hasOwnProperty.call(this,key)){return undefined}return value}}else{if(typeof replacer==="function"||(typeof replacer==="object"&&typeof replacer.length==="number")){rep=replacer}else{throw new Error("JSON.stringify")}}return str("",{"":value})},parse:function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}if(/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")},quote:quote}}()}
+if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return""}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}())
 
 Jemplate.JSON={parse:function(A){return JSON.parse(A)},stringify:function(A){return JSON.stringify(A)}}
 
@@ -55,7 +55,7 @@ sub json_yui {
 
 sub json2 {
     <<'...';
-if(!JSON){var JSON}if(!JSON){JSON=function(){function f(n){return n<10?"0"+n:n}Date.prototype.toJSON=function(){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){return escapeable.test(string)?'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==="string"){return c}c=a.charCodeAt();return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(typeof value.length==="number"&&!(value.propertyIsEnumerable("length"))){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return null}return{stringify:function(value,replacer,space){var i;gap="";indent="";if(space){if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}}if(!replacer){rep=function(key,value){if(!Object.hasOwnProperty.call(this,key)){return undefined}return value}}else{if(typeof replacer==="function"||(typeof replacer==="object"&&typeof replacer.length==="number")){rep=replacer}else{throw new Error("JSON.stringify")}}return str("",{"":value})},parse:function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}if(/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")},quote:quote}}()}
+if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}return""}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}())
 ...
 }
 
@@ -32,7 +32,7 @@ if (typeof Jemplate == 'undefined') {
 Jemplate.VERSION = '0.22';
 
 Jemplate.process = function() {
-    var jemplate = new Jemplate();
+    var jemplate = new Jemplate(Jemplate.prototype.config);
     return jemplate.process.apply(jemplate, arguments);
 }
 
@@ -43,38 +43,83 @@ if (! Jemplate.templateMap)
 
 var proto = Jemplate.prototype = {};
 
+proto.config = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    FILTERS: {},
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+proto.defaults = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+
+Jemplate.init = function(config) {
+ 
+    Jemplate.prototype.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof Jemplate.prototype.config[i] == "undefined") {
+            Jemplate.prototype.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
+}
+
 proto.init = function(config) {
-    this.config = config ||
-    {
-        AUTO_RESET: true,
-        BLOCKS: {},
-        CONTEXT: null,
-        DEBUG_UNDEF: false,
-        DEFAULT: null,
-        ERROR: null,
-        EVAL_JAVASCRIPT: false,
-        FILTERS: {},
-        INCLUDE_PATH: [''],
-        INTERPOLATE: false,
-        OUTPUT: null,
-        PLUGINS: {},
-        POST_PROCESS: [],
-        PRE_PROCESS: [],
-        PROCESS: null,
-        RECURSION: false,
-        STASH: null,
-        TOLERANT: null,
-        VARIABLES: {},
-        WRAPPER: []
-    };
+    
+    this.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof this.config[i] == "undefined") {
+            this.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
 }
 
 proto.process = function(template, data, output) {
     var context = this.config.CONTEXT || new Jemplate.Context();
     context.config = this.config;
 
-    context.stash = this.config.STASH || new Jemplate.Stash();
-    context.stash.__config__ = this.config;
+    context.stash = new Jemplate.Stash(this.config.STASH, this.config);
 
     context.__filter__ = new Jemplate.Filter();
     context.__filter__.config = this.config;
@@ -86,7 +131,17 @@ proto.process = function(template, data, output) {
 
     var proc = function(input) {
         try {
+            if (typeof context.config.PRE_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.PRE_PROCESS];                
+            for (var i = 0; i < context.config.PRE_PROCESS.length; i++) {
+                context.process(context.config.PRE_PROCESS[i]);
+            }
+            
             result = context.process(template, input);
+            
+            if (typeof context.config.POST_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.POST_PROCESS];
+            for (i = 0; i < context.config.POST_PROCESS.length; i++) {
+                context.process(context.config.POST_PROCESS[i]);
+            }
         }
         catch(e) {
             if (! String(e).match(/Jemplate\.STOP\n/))
@@ -168,7 +223,8 @@ proto.plugin = function(name, args) {
         throw "Unknown plugin name ':" + name + "'";
 
     // The Context object (this) is passed as the first argument to the plugin.
-    return new window[name](this, args);
+	var func = eval(name);
+    return new func(this, args);
 }
 
 proto.filter = function(text, name, args) {
@@ -322,8 +378,16 @@ proto.filters.replace = function(text, args) {
 // Jemplate.Stash class
 //------------------------------------------------------------------------------
 if (typeof Jemplate.Stash == 'undefined') {
-    Jemplate.Stash = function() {
-        this.data = {};
+    Jemplate.Stash = function(stash, config) {
+        this.__config__ = config;
+		
+		this.data = {
+			GLOBAL : this.__config__.SCOPE			
+		};
+		this.LOCAL_ANCHOR = {};
+		this.data.LOCAL = this.LOCAL_ANCHOR;
+		
+		this.update(stash);
     };
 }
 
@@ -331,7 +395,10 @@ proto = Jemplate.Stash.prototype;
 
 proto.clone = function(args) {
     var data = this.data;
-    this.data = {};
+    this.data = {
+		GLOBAL : this.__config__.SCOPE
+	};
+	this.data.LOCAL = this.LOCAL_ANCHOR;
     this.update(data);
     this.update(args);
     this.data._PARENT = data;
@@ -344,87 +411,221 @@ proto.declone = function(args) {
 proto.update = function(args) {
     if (typeof args == 'undefined') return;
     for (var key in args) {
-        var value = args[key];
-        this.set(key, value);
+        if (key != 'GLOBAL' && key != 'LOCAL') {
+	        this.set(key, args[key]);
+		}
     }
 }
 
-proto.get = function(key) {
+proto.get = function(ident, args) {
     var root = this.data;
-    if (key instanceof Array) {
-        for (var i = 0; i < key.length; i += 2) {
-            var args = key.slice(i, i+2);
-            args.unshift(root);
-            value = this._dotop.apply(this, args);
+    
+    var value;
+    
+    if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+        
+        if (typeof ident == 'string') {
+            ident = ident.split('.');
+            var newIdent = [];
+            for (var i = 0; i < ident.length; i++) {
+                newIdent.push(ident.replace(/\(.*$/,''));
+                newIdent.push(0);
+            }
+            ident = newIdent;
+        }
+        
+        for (var i = 0; i < ident.length; i += 2) {
+            var dotopArgs = ident.slice(i, i+2);
+            dotopArgs.unshift(root);
+            value = this._dotop.apply(this, dotopArgs);
             if (typeof value == 'undefined')
                 break;
             root = value;
         }
     }
     else {
-        value = this._dotop(root, key);
+        value = this._dotop(root, ident, args);
     }
 
-    if (typeof value == 'undefined') {
+    if (typeof value == 'undefined' || value == null) {
         if (this.__config__.DEBUG_UNDEF)
-            throw("undefined value found while using DEGUG_UNDEF");
+            throw("undefined value found while using DEBUG_UNDEF");
         value = '';
     }
 
     return value;
 }
 
-proto.set = function(key, value, set_default) {
-    if (key instanceof Array) {
-        var data = this.get(key[0]) || {};
-        key = key[2];
-    }
-    else {
-        data = this.data;
+
+
+proto.set = function(ident, value, set_default) {
+    
+    var root, result, error;
+    
+    root = this.data;
+    
+    while (true) {
+        if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+            
+            if (typeof ident == 'string') {
+                ident = ident.split('.');
+                var newIdent = [];
+                for (var i = 0; i < ident.length; i++) {
+                    newIdent.push(ident.replace(/\(.*$/,''));
+                    newIdent.push(0);
+                }
+                ident = newIdent;
+            }
+            
+            for (var i = 0; i < ident.length - 2; i += 2) {
+                var dotopArgs = ident.slice(i, i+2);
+                dotopArgs.unshift(root);
+                dotopArgs.push(1);
+                result = this._dotop.apply(this, dotopArgs);
+                if (typeof value == 'undefined')
+                    break;
+                root = result;
+            }
+            
+            var assignArgs = ident.slice(ident.length-2, ident.length);
+            assignArgs.unshift(root);
+            assignArgs.push(value);
+            assignArgs.push(set_default);
+            
+            
+            result = this._assign.apply(this, assignArgs);
+        } else {
+            result = this._assign(root, ident, 0, value, set_default);
+        }
+        break;
     }
-    if (! (set_default && (typeof data[key] != 'undefined')))
-        data[key] = value;
+    
+    return (typeof result != 'undefined') ? result : '';
 }
 
-proto._dotop = function(root, item, args) {
-    if (typeof item == 'undefined' ||
-        typeof item == 'string' && item.match(/^[\._]/)) {
+
+
+proto._dotop = function(root, item, args, lvalue) {    
+    if (root == this.LOCAL_ANCHOR) root = this.data;
+	var atroot = root == this.data;
+    
+    var value,result = undefined;
+    
+   	var is_function_call = args instanceof Array;
+   	
+   	args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
         return undefined;
     }
 
-    if ((! args) &&
-        (typeof root == 'object') &&
-        (!(root instanceof Array) || (typeof item == 'number')) &&
-        (typeof root[item] != 'undefined')) {
-        var value = root[item];
-        if (typeof value == 'function')
-            value = value.apply(root);
-        return value;
+
+    //root is complex object, not scalar
+    if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) {
+        
+		if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null
+            if (typeof root[item] == 'function') {
+                result = root[item].apply(root,args);
+            } else {
+                return root[item];
+            }
+        } else if (lvalue) {
+            return root[item] = {};
+        } else if (this.hash_functions[item] && !atroot || item == 'import') {
+            args.unshift(root);
+            result = this.hash_functions[item].apply(this,args);
+        } else if (item instanceof Array) {
+            result = {};
+            
+            for (var i = 0; i < item.length; i++) result[item[i]] = root[item[i]];
+            return result;
+        }
+    } else if (root instanceof Array) {
+        if (this.list_functions[item]) {
+            args.unshift(root);
+            result = this.list_functions[item].apply(this,args);
+        } else if (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' ) {
+            if (typeof root[item] != 'function') return root[item];
+            result = root[item].apply(this, args);
+        } else if (item instanceof Array) {
+            for (var i = 0; i < item.length; i++) result.push(root[item[i]]);
+            return result;
+        }
+    } else if (this.string_functions[item] && !lvalue) {
+        args.unshift(root);
+        result = this.string_functions[item].apply(this, args);
+    } else if (this.list_functions[item] && !lvalue) {
+        args.unshift([root]);
+        result = this.list_functions[item].apply(this,args);
+    } else {
+        result = undefined;
     }
+    
+    
+    if (result instanceof Array) {
+		if (typeof result[0] == 'undefined' && typeof result[1] != 'undefined') {
+	        throw result[1];
+	    }
+	}
+    
+    return result;
+
+}
 
-    if (typeof root == 'string' && this.string_functions[item])
-        return this.string_functions[item](root, args);
-    if (root instanceof Array && this.list_functions[item])
-        return this.list_functions[item](root, args);
-    if (typeof root == 'object' && this.hash_functions[item])
-        return this.hash_functions[item](root, args);
-    if (typeof root[item] == 'function')
-        return root[item].apply(root, args);
 
+proto._assign = function(root, item, args, value, set_default) {
+    var atroot = root == this.data;
+    var result;
+    
+    args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
+        return undefined;
+    }
+    
+    if (atroot || root.constructor == Object || root == this.data.GLOBAL) {
+		
+		if (root == this.LOCAL_ANCHOR) root = this.data;
+			 
+		if (!(set_default && typeof root[item] != 'undefined')) {
+            if (atroot && item == 'GLOBAL') throw "Attempt to modify GLOBAL access modifier"
+			if (atroot && item == 'LOCAL') throw "Attempt to modify LOCAL access modifier"
+			
+			return root[item] = value;
+        } 
+    } else if ((root instanceof Array) && (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' )) {
+        if (!(set_default && typeof root[item] != 'undefined')) {
+            return root[item] = value;
+        }
+    } else if ( (root.constructor != Object) && (root instanceof Object) ) {
+        try {
+            result = root[item].apply(root,args);
+        } catch (e) {
+        }
+    } else {
+        throw 'dont know how to assign to [' + root + '.' + item +']';
+    }
+    
     return undefined;
 }
 
+
 proto.string_functions = {};
 
+// typeof
+proto.string_functions['typeof'] = function(value) {
+    return typeof value;
+}
+
 // chunk(size)     negative size chunks from end
-proto.string_functions.chunk = function(string, args) {
-    var size = args[0];
+proto.string_functions.chunk = function(string, size) {
+    //var size = args;
     var list = new Array();
     if (! size)
         size = 1;
     if (size < 0) {
         size = 0 - size;
-        for (i = string.length - size; i >= 0; i = i - size)
+        for (var i = string.length - size; i >= 0; i = i - size)
             list.unshift(string.substr(i, size));
         if (string.length % size)
             list.unshift(string.substr(0, string.length % size));
@@ -456,15 +657,15 @@ proto.string_functions.list = function(string) {
 }
 
 // match(re)       get list of matches
-proto.string_functions.match = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
+proto.string_functions.match = function(string, re, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);
     var list = string.match(regexp);
     return list;
 }
 
 // repeat(n)       repeated n times
 proto.string_functions.repeat = function(string, args) {
-    var n = args[0] || 1;
+    var n = args || 1;
     var output = '';
     for (var i = 0; i < n; i++) {
         output += string;
@@ -472,19 +673,17 @@ proto.string_functions.repeat = function(string, args) {
     return output;
 }
 
-// replace(re, sub)    replace instances of re with sub
-proto.string_functions.replace = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var sub = args[1];
-    if (! sub)
-        sub  = '';
-    var output = string.replace(regexp, sub);
-    return output;
+// replace(re, sub, global)    replace instances of re with sub
+proto.string_functions.replace = function(string, re, sub, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);    
+    if (! sub) sub  = '';
+
+    return string.replace(regexp, sub);
 }
 
 // search(re)      true if value matches re
-proto.string_functions.search = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.search = function(string, re) {
+    var regexp = new RegExp(re);
     return (string.search(regexp) >= 0) ? 1 : 0;
 }
 
@@ -494,8 +693,8 @@ proto.string_functions.size = function(string) {
 }
 
 // split(re)       split string on re
-proto.string_functions.split = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.split = function(string, re) {
+    var regexp = new RegExp(re);
     var list = string.split(regexp);
     return list;
 }
@@ -504,8 +703,18 @@ proto.string_functions.split = function(string, args) {
 
 proto.list_functions = {};
 
-proto.list_functions.join = function(list, args) {
-    return list.join(args[0]);
+// typeof
+proto.list_functions['typeof'] = function(list) {
+    return 'array';
+};
+
+
+proto.list_functions.list = function(list) {
+    return list;
+};
+
+proto.list_functions.join = function(list, str) {
+    return list.join(str);
 };
 
 proto.list_functions.sort = function(list,key) {
@@ -533,8 +742,8 @@ proto.list_functions.nsort = function(list) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
-proto.list_functions.grep = function(list, args) {
-    var regexp = new RegExp(args[0]);
+proto.list_functions.grep = function(list, re) {
+    var regexp = new RegExp(re);
     var result = [];
     for (var i = 0; i < list.length; i++) {
         if (list[i].match(regexp))
@@ -563,7 +772,7 @@ proto.list_functions.reverse = function(list) {
     return result;
 }
 
-proto.list_functions.merge = function(list, args) {
+proto.list_functions.merge = function(list /*, ... args */) {
     var result = [];
     var push_all = function(elem) {
         if (elem instanceof Array) {
@@ -576,28 +785,30 @@ proto.list_functions.merge = function(list, args) {
         }
     }
     push_all(list);
-    for (var i = 0; i < args.length; i++) {
-        push_all(args[i]);
+    for (var i = 1; i < arguments.length; i++) {
+        push_all(arguments[i]);
     }
     return result;
 }
 
-proto.list_functions.slice = function(list, args) {
-    return list.slice(args[0], args[1]);
+proto.list_functions.slice = function(list, start, end) {
+    // To make it like slice in TT
+    // See rt53453
+    if ( end == -1 ) {
+        return list.slice( start );
+    }
+    return list.slice( start, end + 1 );
 }
 
-proto.list_functions.splice = function(list, args) {
-    if (args.length == 1)
-        return list.splice(args[0]);
-    if (args.length == 2)
-        return list.splice(args[0], args[1]);
-    if (args.length == 3)
-        return list.splice(args[0], args[1], args[2]);
-    return null;
+proto.list_functions.splice = function(list /*, ... args */ ) {
+    var args = Array.prototype.slice.call(arguments);
+    args.shift();
+    
+    return list.splice.apply(list,args);
 }
 
-proto.list_functions.push = function(list, args) {
-    list.push(args[0]);
+proto.list_functions.push = function(list, value) {
+    list.push(value);
     return list;
 }
 
@@ -605,8 +816,8 @@ proto.list_functions.pop = function(list) {
     return list.pop();
 }
 
-proto.list_functions.unshift = function(list, args) {
-    list.unshift(args[0]);
+proto.list_functions.unshift = function(list, value) {
+    list.unshift(value);
     return list;
 }
 
@@ -632,6 +843,11 @@ proto.list_functions.last = function(list) {
 
 proto.hash_functions = {};
 
+// typeof
+proto.hash_functions['typeof'] = function(hash) {
+    return 'object';
+};
+
 
 // each            list of alternating keys/values
 proto.hash_functions.each = function(hash) {
@@ -642,20 +858,17 @@ proto.hash_functions.each = function(hash) {
 }
 
 // exists(key)     does key exist?
-proto.hash_functions.exists = function(hash, args) {
-    return ( typeof( hash[args[0]] ) == "undefined" ) ? 0 : 1;
+proto.hash_functions.exists = function(hash, key) {
+    return ( typeof( hash[key] ) == "undefined" ) ? 0 : 1;
 }
 
-// FIXME proto.hash_functions.import blows everything up
-//
 // import(hash2)   import contents of hash2
 // import          import into current namespace hash
-//proto.hash_functions.import = function(hash, args) {
-//    var hash2 = args[0];
-//    for ( var key in hash2 )
-//        hash[key] = hash2[key];
-//    return '';
-//}
+proto.hash_functions['import'] = function(hash, hash2) {    
+    for ( var key in hash2 )
+        hash[key] = hash2[key];
+    return '';
+}
 
 // keys            list of keys
 proto.hash_functions.keys = function(hash) {
@@ -666,10 +879,10 @@ proto.hash_functions.keys = function(hash) {
 }
 
 // list            returns alternating key, value
-proto.hash_functions.list = function(hash, args) {
-    var what = '';
-    if ( args )
-        what = args[0];
+proto.hash_functions.list = function(hash, what) {
+    //var what = '';
+    //if ( args )
+        //what = args[0];
 
     var list = new Array();
     var key;
@@ -697,6 +910,11 @@ proto.hash_functions.nsort = function(hash) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
+// item           return a value by key
+proto.hash_functions.item = function(hash, key) {
+    return hash[key];
+}
+
 // size            number of pairs
 proto.hash_functions.size = function(hash) {
     var size = 0;
@@ -722,9 +940,23 @@ proto.hash_functions.values = function(hash) {
     return list;
 }
 
+proto.hash_functions.pairs = function(hash) {
+    var list = new Array();
+    var keys = new Array();
+    for ( var key in hash ) {
+        keys.push( key );
+    }
+    keys.sort();
+    for ( var key in keys ) {
+        key = keys[key]
+        list.push( { 'key': key, 'value': hash[key] } );
+    }
+    return list;
+}
+
 //  delete
-proto.hash_functions.remove = function(hash, args) {
-    return delete hash[args[0]];
+proto.hash_functions.remove = function(hash, key) {
+    return delete hash[key];
 }
 proto.hash_functions['delete'] = proto.hash_functions.remove;
 
@@ -747,6 +979,9 @@ if (typeof Jemplate.Iterator == 'undefined') {
             this.object_keys = object_keys.sort();
             this.size = object_keys.length;
             this.max  = this.size -1;
+        } else if (typeof object == 'undefined' || object == null || object == '') {
+            this.object = null;
+            this.max  = -1;
         }
     }
 }
@@ -783,7 +1018,7 @@ proto.get_next = function(should_init) {
             return [this.object_keys[index], false];
         }
     } else {
-        if (index < object.length) {
+        if (index <= this.max) {
             this.prev = index > 0 ? object[index - 1] : "";
             this.next = index < this.max ? object[index +1] : "";
             return [object[index], false];
@@ -968,8 +1203,8 @@ sub json_json2_internal {
 var JSON;
 
 /*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -977,44 +1212,48 @@ var JSON;
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
 
-            This is the toJSON method added to Dates:
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -1022,35 +1261,24 @@ var JSON;
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -1061,16 +1289,22 @@ var JSON;
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -1090,45 +1324,57 @@ var JSON;
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -1138,175 +1384,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -1314,123 +1564,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
+
 
+// If the JSON object does not yet have a parse method, give it one.
 
-            parse: function (text, reviver) {
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
+
 
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
 
-// We split the first stage into 4 regexp operations in order to work around
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 
 Jemplate.JSON = {
@@ -1474,8 +1725,8 @@ Jemplate.JSON = {
 sub json2 {
     <<'...';
 /*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -1483,44 +1734,48 @@ sub json2 {
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
 
-            This is the toJSON method added to Dates:
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -1528,35 +1783,24 @@ sub json2 {
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -1567,16 +1811,22 @@ sub json2 {
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -1596,45 +1846,57 @@ sub json2 {
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -1644,175 +1906,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -1820,123 +2086,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
 
 
-            parse: function (text, reviver) {
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
 
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
 
-// We split the first stage into 4 regexp operations in order to work around
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 ...
 }
@@ -5,7 +5,13 @@ use warnings;
 use Template 2.14;
 use Getopt::Long;
 
-our $VERSION = '0.23';
+=head1 VERSION
+
+Version 0.261
+
+=cut
+ 
+our $VERSION = '0.261';
 
 use Jemplate::Parser;
 
@@ -77,7 +83,7 @@ sub main {
     my ($runtime, $compile, $list) = @$jemplate_options{qw/runtime compile list/};
 
     if ($runtime) {
-        runtime_source_code(@$jemplate_options{qw/runtime ajax json xhr xxx compact/});
+        print runtime_source_code(@$jemplate_options{qw/runtime ajax json xhr xxx compact/});
         return unless $compile;
     }
 
@@ -219,8 +225,10 @@ sub recurse_dir {
     my $dir = shift;
     my @files;
     foreach ( File::Find::Rule->file->in( $dir ) ) {
-        # don't include .hidden files
-        unless ($_ =~ '\/\.') { push(@files, $_); }
+        if ( m{/\.[^\.]+} ) {} # Skip ".hidden" files or directories
+        else {
+            push @files, $_;
+        }
     }
     return @files;
 }
@@ -232,7 +240,7 @@ sub make_file_list {
 
     foreach my $arg (@args) {
         unless (-e $arg) { next; } # file exists
-        unless (-s $arg) { next; } # file size > 0
+        unless (-s $arg or -d $arg) { next; } # file size > 0 or directory (for Win platform)
 
         if (-d $arg) {
             foreach my $full ( recurse_dir($arg) ) {
@@ -262,6 +270,8 @@ sub runtime_source_code {
     require Jemplate::Runtime;
     require Jemplate::Runtime::Compact;
 
+    unshift @_, "standard" unless @_;
+
     my ($runtime, $ajax, $json, $xhr, $xxx, $compact) = map { defined $_ ? lc $_ : "" } @_[0 .. 5];
 
     my $Jemplate_Runtime = $compact ? "Jemplate::Runtime::Compact" : "Jemplate::Runtime";
@@ -311,7 +321,7 @@ sub runtime_source_code {
 
     push @runtime, $Jemplate_Runtime->xxx if $xxx;
 
-    print join ";", @runtime;
+    return join ";", @runtime;
 }
 
 #-------------------------------------------------------------------------------
@@ -383,7 +393,7 @@ sub _preamble {
    Template Toolkit. Any changes made to this file will be lost the next
    time the templates are compiled.
 
-   Copyright 2006 - Ingy döt Net - All rights reserved.
+   Copyright 2006-2008 - Ingy döt Net - All rights reserved.
 */
 
 if (typeof(Jemplate) == 'undefined')
@@ -420,6 +430,10 @@ or, with jQuery.js:
         Jemplate.process('my-template.html', data, '#some-div');
     });
 
+From the commandline:
+
+    jemplate --runtime --compile path/to/jemplate/directory/ > jemplate.js
+
 =head1 DESCRIPTION
 
 Jemplate is a templating framework for JavaScript that is built over
@@ -585,12 +599,13 @@ Jemplate now supports almost all the TT directives, including:
   * [% LAST %]
   * [% CLEAR %]
   * [%# this is a comment %]
+  * [% MACRO name(param1, param2) BLOCK %] ... [% END %]  
 
 ALL of the string virtual functions are supported.
 
 ALL of the array virtual functions are supported:
 
-ALL of the hash virtual functions are supported (except for import):
+ALL of the hash virtual functions are supported:
 
 MANY of the standard filters are implemented.
 
@@ -611,8 +626,8 @@ All tests run 100% successful in the above browsers.
 
 =head1 DEVELOPMENT
 
-The bleeding edge code is available via Subversion at
-http://svn.jemplate.net/repo/trunk/
+The bleeding edge code is available via Git at
+git://github.com/ingydotnet/jemplate.git
 
 You can run the runtime tests directly from
 http://svn.jemplate.net/repo/trunk/tests/run/index.html or from the
@@ -655,6 +670,8 @@ David A. Coffey <dacoffey@cogsmith.com>
 
 Robert Krimen <robertkrimen@gmail.com>
 
+Nickolay Platonov <nickolay8@gmail.com>
+
 =head1 COPYRIGHT
 
 Copyright (c) 2006-2008. Ingy döt Net.
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %]
+[% a.b.c = 1 %]
+[% a.b.c %]
+[% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+result = Jemplate.process( 't1', {} )
+areEqual( result, 1 );
+_END_
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %]
+[% FOREACH a = empty %]
+[% END %]
+1
+[% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+result = Jemplate.process( 't1', { empty: [] } )
+areEqual( result, 1 );
+_END_
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %][% apple %] [% apple.list %][% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+apple = [ 1, 2, 3, 4, 5 ]
+result = Jemplate.process( 't1', { apple: apple } )
+areEqual( result, "1,2,3,4,5 1,2,3,4,5" );
+_END_
@@ -0,0 +1,12 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+plan qw/no_plan/;
+
+system( $^X, 'jemplate' );
+
+ok( ! $? );
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %][% result = BLOCK %]Hello, World![% END %][% result %][% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+var result
+
+result = Jemplate.process( 't1', { } )
+areEqual( result, "Hello, World!" )
+_END_
+
@@ -0,0 +1,51 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %][% apple.slice( 0, 2 ).join(' - ') %][% END %]
+[% BLOCK t2 %][% apple.slice( -3, -1 ).join(' - ') %][% END %]
+[% BLOCK t3 %][% apple.slice( -3, -2 ).join(' - ') %][% END %]
+[% BLOCK t4 %][% apple.slice( 3, -2 ).join(' - ') %][% END %]
+[% BLOCK t5 %][% apple.slice( 3, -1 ).join(' - ') %][% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+var result
+var apple = [ 1, 2, 3, 4, 5, 6, 7 ]
+
+result = Jemplate.process( 't1', { apple: apple } )
+areEqual( result, "1 - 2 - 3" );
+
+result = Jemplate.process( 't2', { apple: apple } )
+areEqual( result, "5 - 6 - 7" );
+
+result = Jemplate.process( 't3', { apple: apple } )
+areEqual( result, "5 - 6" );
+
+result = Jemplate.process( 't4', { apple: apple } )
+areEqual( result, "4 - 5 - 6" );
+
+result = Jemplate.process( 't5', { apple: apple } )
+areEqual( result, "4 - 5 - 6 - 7" );
+_END_
+
@@ -0,0 +1,52 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %][% list = [ 1 .. 10 ] %][% list.join(', ') %][% END %]
+[% BLOCK t2 %][% FOREACH ii = [ 1 .. 4 ] %][% ii %] [% END %][% END %]
+[% BLOCK t3 %][% FOREACH ii = [ -1 .. -4 ] %][% ii %] [% END %][% END %]
+[% BLOCK t4 %][% FOREACH ii = [ -4 .. -1 ] %][% ii %] [% END %][% END %]
+_END_
+
+eval {
+$jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %][% broken = [ 1 .. end ] %][% list.join(', ') %][% END %]
+_END_
+};
+like $@, qr{Range expansion is current supported for positive/negative integer values only};
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+var result
+
+result = Jemplate.process( 't1', { } )
+areEqual( result, "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" );
+
+result = Jemplate.process( 't2', { } )
+areEqual( result, "1 2 3 4 " );
+
+result = Jemplate.process( 't3', { } )
+areEqual( result, "" );
+
+result = Jemplate.process( 't4', { } )
+areEqual( result, "-4 -3 -2 -1 " );
+_END_
@@ -0,0 +1,12 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+plan qw/no_plan/;
+
+use Jemplate;
+
+is( scalar Jemplate::recurse_dir( 't/../t/assets/jt/a' ), 1, 'Only find one file, the rest should be hidden' );
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @js;
+
+push @js, $jemplate->compile_template_content( <<_END_, 't0' );
+[% BLOCK t1 %][% 3 DIV 2 %][% END %]
+[% BLOCK t2 %][% 4 DIV 2 %][% END %]
+[% BLOCK t3 %][% 5 DIV 2 %][% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @js, "1;" );
+test_js <<'_END_';
+
+areEqual( Jemplate.process( 't1' ), 1 );
+areEqual( Jemplate.process( 't2' ), 2 );
+areEqual( Jemplate.process( 't3' ), 2 );
+
+_END_
@@ -25,7 +25,7 @@ if (typeof Jemplate == 'undefined') {
 Jemplate.VERSION = '0.22';
 
 Jemplate.process = function() {
-    var jemplate = new Jemplate();
+    var jemplate = new Jemplate(Jemplate.prototype.config);
     return jemplate.process.apply(jemplate, arguments);
 }
 
@@ -36,38 +36,83 @@ if (! Jemplate.templateMap)
 
 var proto = Jemplate.prototype = {};
 
+proto.config = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    FILTERS: {},
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+proto.defaults = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+
+Jemplate.init = function(config) {
+ 
+    Jemplate.prototype.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof Jemplate.prototype.config[i] == "undefined") {
+            Jemplate.prototype.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
+}
+
 proto.init = function(config) {
-    this.config = config ||
-    {
-        AUTO_RESET: true,
-        BLOCKS: {},
-        CONTEXT: null,
-        DEBUG_UNDEF: false,
-        DEFAULT: null,
-        ERROR: null,
-        EVAL_JAVASCRIPT: false,
-        FILTERS: {},
-        INCLUDE_PATH: [''],
-        INTERPOLATE: false,
-        OUTPUT: null,
-        PLUGINS: {},
-        POST_PROCESS: [],
-        PRE_PROCESS: [],
-        PROCESS: null,
-        RECURSION: false,
-        STASH: null,
-        TOLERANT: null,
-        VARIABLES: {},
-        WRAPPER: []
-    };
+    
+    this.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof this.config[i] == "undefined") {
+            this.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
 }
 
 proto.process = function(template, data, output) {
     var context = this.config.CONTEXT || new Jemplate.Context();
     context.config = this.config;
 
-    context.stash = this.config.STASH || new Jemplate.Stash();
-    context.stash.__config__ = this.config;
+    context.stash = new Jemplate.Stash(this.config.STASH, this.config);
 
     context.__filter__ = new Jemplate.Filter();
     context.__filter__.config = this.config;
@@ -79,7 +124,17 @@ proto.process = function(template, data, output) {
 
     var proc = function(input) {
         try {
+            if (typeof context.config.PRE_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.PRE_PROCESS];                
+            for (var i = 0; i < context.config.PRE_PROCESS.length; i++) {
+                context.process(context.config.PRE_PROCESS[i]);
+            }
+            
             result = context.process(template, input);
+            
+            if (typeof context.config.POST_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.POST_PROCESS];
+            for (i = 0; i < context.config.POST_PROCESS.length; i++) {
+                context.process(context.config.POST_PROCESS[i]);
+            }
         }
         catch(e) {
             if (! String(e).match(/Jemplate\.STOP\n/))
@@ -161,7 +216,8 @@ proto.plugin = function(name, args) {
         throw "Unknown plugin name ':" + name + "'";
 
     // The Context object (this) is passed as the first argument to the plugin.
-    return new window[name](this, args);
+	var func = eval(name);
+    return new func(this, args);
 }
 
 proto.filter = function(text, name, args) {
@@ -315,8 +371,16 @@ proto.filters.replace = function(text, args) {
 // Jemplate.Stash class
 //------------------------------------------------------------------------------
 if (typeof Jemplate.Stash == 'undefined') {
-    Jemplate.Stash = function() {
-        this.data = {};
+    Jemplate.Stash = function(stash, config) {
+        this.__config__ = config;
+		
+		this.data = {
+			GLOBAL : this.__config__.SCOPE			
+		};
+		this.LOCAL_ANCHOR = {};
+		this.data.LOCAL = this.LOCAL_ANCHOR;
+		
+		this.update(stash);
     };
 }
 
@@ -324,7 +388,10 @@ proto = Jemplate.Stash.prototype;
 
 proto.clone = function(args) {
     var data = this.data;
-    this.data = {};
+    this.data = {
+		GLOBAL : this.__config__.SCOPE
+	};
+	this.data.LOCAL = this.LOCAL_ANCHOR;
     this.update(data);
     this.update(args);
     this.data._PARENT = data;
@@ -337,87 +404,221 @@ proto.declone = function(args) {
 proto.update = function(args) {
     if (typeof args == 'undefined') return;
     for (var key in args) {
-        var value = args[key];
-        this.set(key, value);
+        if (key != 'GLOBAL' && key != 'LOCAL') {
+	        this.set(key, args[key]);
+		}
     }
 }
 
-proto.get = function(key) {
+proto.get = function(ident, args) {
     var root = this.data;
-    if (key instanceof Array) {
-        for (var i = 0; i < key.length; i += 2) {
-            var args = key.slice(i, i+2);
-            args.unshift(root);
-            value = this._dotop.apply(this, args);
+    
+    var value;
+    
+    if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+        
+        if (typeof ident == 'string') {
+            ident = ident.split('.');
+            var newIdent = [];
+            for (var i = 0; i < ident.length; i++) {
+                newIdent.push(ident.replace(/\(.*$/,''));
+                newIdent.push(0);
+            }
+            ident = newIdent;
+        }
+        
+        for (var i = 0; i < ident.length; i += 2) {
+            var dotopArgs = ident.slice(i, i+2);
+            dotopArgs.unshift(root);
+            value = this._dotop.apply(this, dotopArgs);
             if (typeof value == 'undefined')
                 break;
             root = value;
         }
     }
     else {
-        value = this._dotop(root, key);
+        value = this._dotop(root, ident, args);
     }
 
-    if (typeof value == 'undefined') {
+    if (typeof value == 'undefined' || value == null) {
         if (this.__config__.DEBUG_UNDEF)
-            throw("undefined value found while using DEGUG_UNDEF");
+            throw("undefined value found while using DEBUG_UNDEF");
         value = '';
     }
 
     return value;
 }
 
-proto.set = function(key, value, set_default) {
-    if (key instanceof Array) {
-        var data = this.get(key[0]) || {};
-        key = key[2];
-    }
-    else {
-        data = this.data;
+
+
+proto.set = function(ident, value, set_default) {
+    
+    var root, result, error;
+    
+    root = this.data;
+    
+    while (true) {
+        if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+            
+            if (typeof ident == 'string') {
+                ident = ident.split('.');
+                var newIdent = [];
+                for (var i = 0; i < ident.length; i++) {
+                    newIdent.push(ident.replace(/\(.*$/,''));
+                    newIdent.push(0);
+                }
+                ident = newIdent;
+            }
+            
+            for (var i = 0; i < ident.length - 2; i += 2) {
+                var dotopArgs = ident.slice(i, i+2);
+                dotopArgs.unshift(root);
+                dotopArgs.push(1);
+                result = this._dotop.apply(this, dotopArgs);
+                if (typeof value == 'undefined')
+                    break;
+                root = result;
+            }
+            
+            var assignArgs = ident.slice(ident.length-2, ident.length);
+            assignArgs.unshift(root);
+            assignArgs.push(value);
+            assignArgs.push(set_default);
+            
+            
+            result = this._assign.apply(this, assignArgs);
+        } else {
+            result = this._assign(root, ident, 0, value, set_default);
+        }
+        break;
     }
-    if (! (set_default && (typeof data[key] != 'undefined')))
-        data[key] = value;
+    
+    return (typeof result != 'undefined') ? result : '';
 }
 
-proto._dotop = function(root, item, args) {
-    if (typeof item == 'undefined' ||
-        typeof item == 'string' && item.match(/^[\._]/)) {
+
+
+proto._dotop = function(root, item, args, lvalue) {    
+    if (root == this.LOCAL_ANCHOR) root = this.data;
+	var atroot = root == this.data;
+    
+    var value,result = undefined;
+    
+   	var is_function_call = args instanceof Array;
+   	
+   	args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
         return undefined;
     }
 
-    if ((! args) &&
-        (typeof root == 'object') &&
-        (!(root instanceof Array) || (typeof item == 'number')) &&
-        (typeof root[item] != 'undefined')) {
-        var value = root[item];
-        if (typeof value == 'function')
-            value = value.apply(root);
-        return value;
+
+    //root is complex object, not scalar
+    if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) {
+        
+		if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null
+            if (typeof root[item] == 'function') {
+                result = root[item].apply(root,args);
+            } else {
+                return root[item];
+            }
+        } else if (lvalue) {
+            return root[item] = {};
+        } else if (this.hash_functions[item] && !atroot || item == 'import') {
+            args.unshift(root);
+            result = this.hash_functions[item].apply(this,args);
+        } else if (item instanceof Array) {
+            result = {};
+            
+            for (var i = 0; i < item.length; i++) result[item[i]] = root[item[i]];
+            return result;
+        }
+    } else if (root instanceof Array) {
+        if (this.list_functions[item]) {
+            args.unshift(root);
+            result = this.list_functions[item].apply(this,args);
+        } else if (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' ) {
+            if (typeof root[item] != 'function') return root[item];
+            result = root[item].apply(this, args);
+        } else if (item instanceof Array) {
+            for (var i = 0; i < item.length; i++) result.push(root[item[i]]);
+            return result;
+        }
+    } else if (this.string_functions[item] && !lvalue) {
+        args.unshift(root);
+        result = this.string_functions[item].apply(this, args);
+    } else if (this.list_functions[item] && !lvalue) {
+        args.unshift([root]);
+        result = this.list_functions[item].apply(this,args);
+    } else {
+        result = undefined;
     }
+    
+    
+    if (result instanceof Array) {
+		if (typeof result[0] == 'undefined' && typeof result[1] != 'undefined') {
+	        throw result[1];
+	    }
+	}
+    
+    return result;
+
+}
 
-    if (typeof root == 'string' && this.string_functions[item])
-        return this.string_functions[item](root, args);
-    if (root instanceof Array && this.list_functions[item])
-        return this.list_functions[item](root, args);
-    if (typeof root == 'object' && this.hash_functions[item])
-        return this.hash_functions[item](root, args);
-    if (typeof root[item] == 'function')
-        return root[item].apply(root, args);
 
+proto._assign = function(root, item, args, value, set_default) {
+    var atroot = root == this.data;
+    var result;
+    
+    args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
+        return undefined;
+    }
+    
+    if (atroot || root.constructor == Object || root == this.data.GLOBAL) {
+		
+		if (root == this.LOCAL_ANCHOR) root = this.data;
+			 
+		if (!(set_default && typeof root[item] != 'undefined')) {
+            if (atroot && item == 'GLOBAL') throw "Attempt to modify GLOBAL access modifier"
+			if (atroot && item == 'LOCAL') throw "Attempt to modify LOCAL access modifier"
+			
+			return root[item] = value;
+        } 
+    } else if ((root instanceof Array) && (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' )) {
+        if (!(set_default && typeof root[item] != 'undefined')) {
+            return root[item] = value;
+        }
+    } else if ( (root.constructor != Object) && (root instanceof Object) ) {
+        try {
+            result = root[item].apply(root,args);
+        } catch (e) {
+        }
+    } else {
+        throw 'dont know how to assign to [' + root + '.' + item +']';
+    }
+    
     return undefined;
 }
 
+
 proto.string_functions = {};
 
+// typeof
+proto.string_functions['typeof'] = function(value) {
+    return typeof value;
+}
+
 // chunk(size)     negative size chunks from end
-proto.string_functions.chunk = function(string, args) {
-    var size = args[0];
+proto.string_functions.chunk = function(string, size) {
+    //var size = args;
     var list = new Array();
     if (! size)
         size = 1;
     if (size < 0) {
         size = 0 - size;
-        for (i = string.length - size; i >= 0; i = i - size)
+        for (var i = string.length - size; i >= 0; i = i - size)
             list.unshift(string.substr(i, size));
         if (string.length % size)
             list.unshift(string.substr(0, string.length % size));
@@ -449,15 +650,15 @@ proto.string_functions.list = function(string) {
 }
 
 // match(re)       get list of matches
-proto.string_functions.match = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
+proto.string_functions.match = function(string, re, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);
     var list = string.match(regexp);
     return list;
 }
 
 // repeat(n)       repeated n times
 proto.string_functions.repeat = function(string, args) {
-    var n = args[0] || 1;
+    var n = args || 1;
     var output = '';
     for (var i = 0; i < n; i++) {
         output += string;
@@ -465,19 +666,17 @@ proto.string_functions.repeat = function(string, args) {
     return output;
 }
 
-// replace(re, sub)    replace instances of re with sub
-proto.string_functions.replace = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var sub = args[1];
-    if (! sub)
-        sub  = '';
-    var output = string.replace(regexp, sub);
-    return output;
+// replace(re, sub, global)    replace instances of re with sub
+proto.string_functions.replace = function(string, re, sub, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);    
+    if (! sub) sub  = '';
+
+    return string.replace(regexp, sub);
 }
 
 // search(re)      true if value matches re
-proto.string_functions.search = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.search = function(string, re) {
+    var regexp = new RegExp(re);
     return (string.search(regexp) >= 0) ? 1 : 0;
 }
 
@@ -487,8 +686,8 @@ proto.string_functions.size = function(string) {
 }
 
 // split(re)       split string on re
-proto.string_functions.split = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.split = function(string, re) {
+    var regexp = new RegExp(re);
     var list = string.split(regexp);
     return list;
 }
@@ -497,8 +696,18 @@ proto.string_functions.split = function(string, args) {
 
 proto.list_functions = {};
 
-proto.list_functions.join = function(list, args) {
-    return list.join(args[0]);
+// typeof
+proto.list_functions['typeof'] = function(list) {
+    return 'array';
+};
+
+
+proto.list_functions.list = function(list) {
+    return list;
+};
+
+proto.list_functions.join = function(list, str) {
+    return list.join(str);
 };
 
 proto.list_functions.sort = function(list,key) {
@@ -526,8 +735,8 @@ proto.list_functions.nsort = function(list) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
-proto.list_functions.grep = function(list, args) {
-    var regexp = new RegExp(args[0]);
+proto.list_functions.grep = function(list, re) {
+    var regexp = new RegExp(re);
     var result = [];
     for (var i = 0; i < list.length; i++) {
         if (list[i].match(regexp))
@@ -556,7 +765,7 @@ proto.list_functions.reverse = function(list) {
     return result;
 }
 
-proto.list_functions.merge = function(list, args) {
+proto.list_functions.merge = function(list /*, ... args */) {
     var result = [];
     var push_all = function(elem) {
         if (elem instanceof Array) {
@@ -569,28 +778,30 @@ proto.list_functions.merge = function(list, args) {
         }
     }
     push_all(list);
-    for (var i = 0; i < args.length; i++) {
-        push_all(args[i]);
+    for (var i = 1; i < arguments.length; i++) {
+        push_all(arguments[i]);
     }
     return result;
 }
 
-proto.list_functions.slice = function(list, args) {
-    return list.slice(args[0], args[1]);
+proto.list_functions.slice = function(list, start, end) {
+    // To make it like slice in TT
+    // See rt53453
+    if ( end == -1 ) {
+        return list.slice( start );
+    }
+    return list.slice( start, end + 1 );
 }
 
-proto.list_functions.splice = function(list, args) {
-    if (args.length == 1)
-        return list.splice(args[0]);
-    if (args.length == 2)
-        return list.splice(args[0], args[1]);
-    if (args.length == 3)
-        return list.splice(args[0], args[1], args[2]);
-    return null;
+proto.list_functions.splice = function(list /*, ... args */ ) {
+    var args = Array.prototype.slice.call(arguments);
+    args.shift();
+    
+    return list.splice.apply(list,args);
 }
 
-proto.list_functions.push = function(list, args) {
-    list.push(args[0]);
+proto.list_functions.push = function(list, value) {
+    list.push(value);
     return list;
 }
 
@@ -598,8 +809,8 @@ proto.list_functions.pop = function(list) {
     return list.pop();
 }
 
-proto.list_functions.unshift = function(list, args) {
-    list.unshift(args[0]);
+proto.list_functions.unshift = function(list, value) {
+    list.unshift(value);
     return list;
 }
 
@@ -625,6 +836,11 @@ proto.list_functions.last = function(list) {
 
 proto.hash_functions = {};
 
+// typeof
+proto.hash_functions['typeof'] = function(hash) {
+    return 'object';
+};
+
 
 // each            list of alternating keys/values
 proto.hash_functions.each = function(hash) {
@@ -635,20 +851,17 @@ proto.hash_functions.each = function(hash) {
 }
 
 // exists(key)     does key exist?
-proto.hash_functions.exists = function(hash, args) {
-    return ( typeof( hash[args[0]] ) == "undefined" ) ? 0 : 1;
+proto.hash_functions.exists = function(hash, key) {
+    return ( typeof( hash[key] ) == "undefined" ) ? 0 : 1;
 }
 
-// FIXME proto.hash_functions.import blows everything up
-//
 // import(hash2)   import contents of hash2
 // import          import into current namespace hash
-//proto.hash_functions.import = function(hash, args) {
-//    var hash2 = args[0];
-//    for ( var key in hash2 )
-//        hash[key] = hash2[key];
-//    return '';
-//}
+proto.hash_functions['import'] = function(hash, hash2) {    
+    for ( var key in hash2 )
+        hash[key] = hash2[key];
+    return '';
+}
 
 // keys            list of keys
 proto.hash_functions.keys = function(hash) {
@@ -659,10 +872,10 @@ proto.hash_functions.keys = function(hash) {
 }
 
 // list            returns alternating key, value
-proto.hash_functions.list = function(hash, args) {
-    var what = '';
-    if ( args )
-        what = args[0];
+proto.hash_functions.list = function(hash, what) {
+    //var what = '';
+    //if ( args )
+        //what = args[0];
 
     var list = new Array();
     var key;
@@ -690,6 +903,11 @@ proto.hash_functions.nsort = function(hash) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
+// item           return a value by key
+proto.hash_functions.item = function(hash, key) {
+    return hash[key];
+}
+
 // size            number of pairs
 proto.hash_functions.size = function(hash) {
     var size = 0;
@@ -715,9 +933,23 @@ proto.hash_functions.values = function(hash) {
     return list;
 }
 
+proto.hash_functions.pairs = function(hash) {
+    var list = new Array();
+    var keys = new Array();
+    for ( var key in hash ) {
+        keys.push( key );
+    }
+    keys.sort();
+    for ( var key in keys ) {
+        key = keys[key]
+        list.push( { 'key': key, 'value': hash[key] } );
+    }
+    return list;
+}
+
 //  delete
-proto.hash_functions.remove = function(hash, args) {
-    return delete hash[args[0]];
+proto.hash_functions.remove = function(hash, key) {
+    return delete hash[key];
 }
 proto.hash_functions['delete'] = proto.hash_functions.remove;
 
@@ -740,6 +972,9 @@ if (typeof Jemplate.Iterator == 'undefined') {
             this.object_keys = object_keys.sort();
             this.size = object_keys.length;
             this.max  = this.size -1;
+        } else if (typeof object == 'undefined' || object == null || object == '') {
+            this.object = null;
+            this.max  = -1;
         }
     }
 }
@@ -776,7 +1011,7 @@ proto.get_next = function(should_init) {
             return [this.object_keys[index], false];
         }
     } else {
-        if (index < object.length) {
+        if (index <= this.max) {
             this.prev = index > 0 ? object[index - 1] : "";
             this.next = index < this.max ? object[index +1] : "";
             return [object[index], false];
@@ -818,8 +1053,8 @@ Jemplate.JSON = {
 }());
 
 ;/*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -827,44 +1062,48 @@ Jemplate.JSON = {
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
 
-            This is the toJSON method added to Dates:
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -872,35 +1111,24 @@ Jemplate.JSON = {
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -911,16 +1139,22 @@ Jemplate.JSON = {
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -940,45 +1174,57 @@ Jemplate.JSON = {
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -988,175 +1234,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -1164,123 +1414,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
+
 
+// If the JSON object does not yet have a parse method, give it one.
 
-            parse: function (text, reviver) {
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
 
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
 
-// We split the first stage into 4 regexp operations in order to work around
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 ;;(function(){
 
diff --git a/var/tmp/source/RKRIMEN/Jemplate-0.261/Jemplate-0.261/t/assets/jt/a/.hidden/apple b/var/tmp/source/RKRIMEN/Jemplate-0.261/Jemplate-0.261/t/assets/jt/a/.hidden/apple
new file mode 100644
index 00000000..e69de29b
diff --git a/var/tmp/source/RKRIMEN/Jemplate-0.261/Jemplate-0.261/t/assets/jt/a/b/c/.hidden b/var/tmp/source/RKRIMEN/Jemplate-0.261/Jemplate-0.261/t/assets/jt/a/b/c/.hidden
new file mode 100644
index 00000000..e69de29b
@@ -0,0 +1,3 @@
+[% BLOCK cherry %]
+apple
+[% END %]
@@ -25,7 +25,7 @@ if (typeof Jemplate == 'undefined') {
 Jemplate.VERSION = '0.22';
 
 Jemplate.process = function() {
-    var jemplate = new Jemplate();
+    var jemplate = new Jemplate(Jemplate.prototype.config);
     return jemplate.process.apply(jemplate, arguments);
 }
 
@@ -36,38 +36,83 @@ if (! Jemplate.templateMap)
 
 var proto = Jemplate.prototype = {};
 
+proto.config = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    FILTERS: {},
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+proto.defaults = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+
+Jemplate.init = function(config) {
+ 
+    Jemplate.prototype.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof Jemplate.prototype.config[i] == "undefined") {
+            Jemplate.prototype.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
+}
+
 proto.init = function(config) {
-    this.config = config ||
-    {
-        AUTO_RESET: true,
-        BLOCKS: {},
-        CONTEXT: null,
-        DEBUG_UNDEF: false,
-        DEFAULT: null,
-        ERROR: null,
-        EVAL_JAVASCRIPT: false,
-        FILTERS: {},
-        INCLUDE_PATH: [''],
-        INTERPOLATE: false,
-        OUTPUT: null,
-        PLUGINS: {},
-        POST_PROCESS: [],
-        PRE_PROCESS: [],
-        PROCESS: null,
-        RECURSION: false,
-        STASH: null,
-        TOLERANT: null,
-        VARIABLES: {},
-        WRAPPER: []
-    };
+    
+    this.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof this.config[i] == "undefined") {
+            this.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
 }
 
 proto.process = function(template, data, output) {
     var context = this.config.CONTEXT || new Jemplate.Context();
     context.config = this.config;
 
-    context.stash = this.config.STASH || new Jemplate.Stash();
-    context.stash.__config__ = this.config;
+    context.stash = new Jemplate.Stash(this.config.STASH, this.config);
 
     context.__filter__ = new Jemplate.Filter();
     context.__filter__.config = this.config;
@@ -79,7 +124,17 @@ proto.process = function(template, data, output) {
 
     var proc = function(input) {
         try {
+            if (typeof context.config.PRE_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.PRE_PROCESS];                
+            for (var i = 0; i < context.config.PRE_PROCESS.length; i++) {
+                context.process(context.config.PRE_PROCESS[i]);
+            }
+            
             result = context.process(template, input);
+            
+            if (typeof context.config.POST_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.POST_PROCESS];
+            for (i = 0; i < context.config.POST_PROCESS.length; i++) {
+                context.process(context.config.POST_PROCESS[i]);
+            }
         }
         catch(e) {
             if (! String(e).match(/Jemplate\.STOP\n/))
@@ -161,7 +216,8 @@ proto.plugin = function(name, args) {
         throw "Unknown plugin name ':" + name + "'";
 
     // The Context object (this) is passed as the first argument to the plugin.
-    return new window[name](this, args);
+	var func = eval(name);
+    return new func(this, args);
 }
 
 proto.filter = function(text, name, args) {
@@ -315,8 +371,16 @@ proto.filters.replace = function(text, args) {
 // Jemplate.Stash class
 //------------------------------------------------------------------------------
 if (typeof Jemplate.Stash == 'undefined') {
-    Jemplate.Stash = function() {
-        this.data = {};
+    Jemplate.Stash = function(stash, config) {
+        this.__config__ = config;
+		
+		this.data = {
+			GLOBAL : this.__config__.SCOPE			
+		};
+		this.LOCAL_ANCHOR = {};
+		this.data.LOCAL = this.LOCAL_ANCHOR;
+		
+		this.update(stash);
     };
 }
 
@@ -324,7 +388,10 @@ proto = Jemplate.Stash.prototype;
 
 proto.clone = function(args) {
     var data = this.data;
-    this.data = {};
+    this.data = {
+		GLOBAL : this.__config__.SCOPE
+	};
+	this.data.LOCAL = this.LOCAL_ANCHOR;
     this.update(data);
     this.update(args);
     this.data._PARENT = data;
@@ -337,87 +404,221 @@ proto.declone = function(args) {
 proto.update = function(args) {
     if (typeof args == 'undefined') return;
     for (var key in args) {
-        var value = args[key];
-        this.set(key, value);
+        if (key != 'GLOBAL' && key != 'LOCAL') {
+	        this.set(key, args[key]);
+		}
     }
 }
 
-proto.get = function(key) {
+proto.get = function(ident, args) {
     var root = this.data;
-    if (key instanceof Array) {
-        for (var i = 0; i < key.length; i += 2) {
-            var args = key.slice(i, i+2);
-            args.unshift(root);
-            value = this._dotop.apply(this, args);
+    
+    var value;
+    
+    if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+        
+        if (typeof ident == 'string') {
+            ident = ident.split('.');
+            var newIdent = [];
+            for (var i = 0; i < ident.length; i++) {
+                newIdent.push(ident.replace(/\(.*$/,''));
+                newIdent.push(0);
+            }
+            ident = newIdent;
+        }
+        
+        for (var i = 0; i < ident.length; i += 2) {
+            var dotopArgs = ident.slice(i, i+2);
+            dotopArgs.unshift(root);
+            value = this._dotop.apply(this, dotopArgs);
             if (typeof value == 'undefined')
                 break;
             root = value;
         }
     }
     else {
-        value = this._dotop(root, key);
+        value = this._dotop(root, ident, args);
     }
 
-    if (typeof value == 'undefined') {
+    if (typeof value == 'undefined' || value == null) {
         if (this.__config__.DEBUG_UNDEF)
-            throw("undefined value found while using DEGUG_UNDEF");
+            throw("undefined value found while using DEBUG_UNDEF");
         value = '';
     }
 
     return value;
 }
 
-proto.set = function(key, value, set_default) {
-    if (key instanceof Array) {
-        var data = this.get(key[0]) || {};
-        key = key[2];
-    }
-    else {
-        data = this.data;
+
+
+proto.set = function(ident, value, set_default) {
+    
+    var root, result, error;
+    
+    root = this.data;
+    
+    while (true) {
+        if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+            
+            if (typeof ident == 'string') {
+                ident = ident.split('.');
+                var newIdent = [];
+                for (var i = 0; i < ident.length; i++) {
+                    newIdent.push(ident.replace(/\(.*$/,''));
+                    newIdent.push(0);
+                }
+                ident = newIdent;
+            }
+            
+            for (var i = 0; i < ident.length - 2; i += 2) {
+                var dotopArgs = ident.slice(i, i+2);
+                dotopArgs.unshift(root);
+                dotopArgs.push(1);
+                result = this._dotop.apply(this, dotopArgs);
+                if (typeof value == 'undefined')
+                    break;
+                root = result;
+            }
+            
+            var assignArgs = ident.slice(ident.length-2, ident.length);
+            assignArgs.unshift(root);
+            assignArgs.push(value);
+            assignArgs.push(set_default);
+            
+            
+            result = this._assign.apply(this, assignArgs);
+        } else {
+            result = this._assign(root, ident, 0, value, set_default);
+        }
+        break;
     }
-    if (! (set_default && (typeof data[key] != 'undefined')))
-        data[key] = value;
+    
+    return (typeof result != 'undefined') ? result : '';
 }
 
-proto._dotop = function(root, item, args) {
-    if (typeof item == 'undefined' ||
-        typeof item == 'string' && item.match(/^[\._]/)) {
+
+
+proto._dotop = function(root, item, args, lvalue) {    
+    if (root == this.LOCAL_ANCHOR) root = this.data;
+	var atroot = root == this.data;
+    
+    var value,result = undefined;
+    
+   	var is_function_call = args instanceof Array;
+   	
+   	args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
         return undefined;
     }
 
-    if ((! args) &&
-        (typeof root == 'object') &&
-        (!(root instanceof Array) || (typeof item == 'number')) &&
-        (typeof root[item] != 'undefined')) {
-        var value = root[item];
-        if (typeof value == 'function')
-            value = value.apply(root);
-        return value;
+
+    //root is complex object, not scalar
+    if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) {
+        
+		if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null
+            if (typeof root[item] == 'function') {
+                result = root[item].apply(root,args);
+            } else {
+                return root[item];
+            }
+        } else if (lvalue) {
+            return root[item] = {};
+        } else if (this.hash_functions[item] && !atroot || item == 'import') {
+            args.unshift(root);
+            result = this.hash_functions[item].apply(this,args);
+        } else if (item instanceof Array) {
+            result = {};
+            
+            for (var i = 0; i < item.length; i++) result[item[i]] = root[item[i]];
+            return result;
+        }
+    } else if (root instanceof Array) {
+        if (this.list_functions[item]) {
+            args.unshift(root);
+            result = this.list_functions[item].apply(this,args);
+        } else if (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' ) {
+            if (typeof root[item] != 'function') return root[item];
+            result = root[item].apply(this, args);
+        } else if (item instanceof Array) {
+            for (var i = 0; i < item.length; i++) result.push(root[item[i]]);
+            return result;
+        }
+    } else if (this.string_functions[item] && !lvalue) {
+        args.unshift(root);
+        result = this.string_functions[item].apply(this, args);
+    } else if (this.list_functions[item] && !lvalue) {
+        args.unshift([root]);
+        result = this.list_functions[item].apply(this,args);
+    } else {
+        result = undefined;
     }
+    
+    
+    if (result instanceof Array) {
+		if (typeof result[0] == 'undefined' && typeof result[1] != 'undefined') {
+	        throw result[1];
+	    }
+	}
+    
+    return result;
+
+}
 
-    if (typeof root == 'string' && this.string_functions[item])
-        return this.string_functions[item](root, args);
-    if (root instanceof Array && this.list_functions[item])
-        return this.list_functions[item](root, args);
-    if (typeof root == 'object' && this.hash_functions[item])
-        return this.hash_functions[item](root, args);
-    if (typeof root[item] == 'function')
-        return root[item].apply(root, args);
 
+proto._assign = function(root, item, args, value, set_default) {
+    var atroot = root == this.data;
+    var result;
+    
+    args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
+        return undefined;
+    }
+    
+    if (atroot || root.constructor == Object || root == this.data.GLOBAL) {
+		
+		if (root == this.LOCAL_ANCHOR) root = this.data;
+			 
+		if (!(set_default && typeof root[item] != 'undefined')) {
+            if (atroot && item == 'GLOBAL') throw "Attempt to modify GLOBAL access modifier"
+			if (atroot && item == 'LOCAL') throw "Attempt to modify LOCAL access modifier"
+			
+			return root[item] = value;
+        } 
+    } else if ((root instanceof Array) && (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' )) {
+        if (!(set_default && typeof root[item] != 'undefined')) {
+            return root[item] = value;
+        }
+    } else if ( (root.constructor != Object) && (root instanceof Object) ) {
+        try {
+            result = root[item].apply(root,args);
+        } catch (e) {
+        }
+    } else {
+        throw 'dont know how to assign to [' + root + '.' + item +']';
+    }
+    
     return undefined;
 }
 
+
 proto.string_functions = {};
 
+// typeof
+proto.string_functions['typeof'] = function(value) {
+    return typeof value;
+}
+
 // chunk(size)     negative size chunks from end
-proto.string_functions.chunk = function(string, args) {
-    var size = args[0];
+proto.string_functions.chunk = function(string, size) {
+    //var size = args;
     var list = new Array();
     if (! size)
         size = 1;
     if (size < 0) {
         size = 0 - size;
-        for (i = string.length - size; i >= 0; i = i - size)
+        for (var i = string.length - size; i >= 0; i = i - size)
             list.unshift(string.substr(i, size));
         if (string.length % size)
             list.unshift(string.substr(0, string.length % size));
@@ -449,15 +650,15 @@ proto.string_functions.list = function(string) {
 }
 
 // match(re)       get list of matches
-proto.string_functions.match = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
+proto.string_functions.match = function(string, re, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);
     var list = string.match(regexp);
     return list;
 }
 
 // repeat(n)       repeated n times
 proto.string_functions.repeat = function(string, args) {
-    var n = args[0] || 1;
+    var n = args || 1;
     var output = '';
     for (var i = 0; i < n; i++) {
         output += string;
@@ -465,19 +666,17 @@ proto.string_functions.repeat = function(string, args) {
     return output;
 }
 
-// replace(re, sub)    replace instances of re with sub
-proto.string_functions.replace = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var sub = args[1];
-    if (! sub)
-        sub  = '';
-    var output = string.replace(regexp, sub);
-    return output;
+// replace(re, sub, global)    replace instances of re with sub
+proto.string_functions.replace = function(string, re, sub, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);    
+    if (! sub) sub  = '';
+
+    return string.replace(regexp, sub);
 }
 
 // search(re)      true if value matches re
-proto.string_functions.search = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.search = function(string, re) {
+    var regexp = new RegExp(re);
     return (string.search(regexp) >= 0) ? 1 : 0;
 }
 
@@ -487,8 +686,8 @@ proto.string_functions.size = function(string) {
 }
 
 // split(re)       split string on re
-proto.string_functions.split = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.split = function(string, re) {
+    var regexp = new RegExp(re);
     var list = string.split(regexp);
     return list;
 }
@@ -497,8 +696,18 @@ proto.string_functions.split = function(string, args) {
 
 proto.list_functions = {};
 
-proto.list_functions.join = function(list, args) {
-    return list.join(args[0]);
+// typeof
+proto.list_functions['typeof'] = function(list) {
+    return 'array';
+};
+
+
+proto.list_functions.list = function(list) {
+    return list;
+};
+
+proto.list_functions.join = function(list, str) {
+    return list.join(str);
 };
 
 proto.list_functions.sort = function(list,key) {
@@ -526,8 +735,8 @@ proto.list_functions.nsort = function(list) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
-proto.list_functions.grep = function(list, args) {
-    var regexp = new RegExp(args[0]);
+proto.list_functions.grep = function(list, re) {
+    var regexp = new RegExp(re);
     var result = [];
     for (var i = 0; i < list.length; i++) {
         if (list[i].match(regexp))
@@ -556,7 +765,7 @@ proto.list_functions.reverse = function(list) {
     return result;
 }
 
-proto.list_functions.merge = function(list, args) {
+proto.list_functions.merge = function(list /*, ... args */) {
     var result = [];
     var push_all = function(elem) {
         if (elem instanceof Array) {
@@ -569,28 +778,30 @@ proto.list_functions.merge = function(list, args) {
         }
     }
     push_all(list);
-    for (var i = 0; i < args.length; i++) {
-        push_all(args[i]);
+    for (var i = 1; i < arguments.length; i++) {
+        push_all(arguments[i]);
     }
     return result;
 }
 
-proto.list_functions.slice = function(list, args) {
-    return list.slice(args[0], args[1]);
+proto.list_functions.slice = function(list, start, end) {
+    // To make it like slice in TT
+    // See rt53453
+    if ( end == -1 ) {
+        return list.slice( start );
+    }
+    return list.slice( start, end + 1 );
 }
 
-proto.list_functions.splice = function(list, args) {
-    if (args.length == 1)
-        return list.splice(args[0]);
-    if (args.length == 2)
-        return list.splice(args[0], args[1]);
-    if (args.length == 3)
-        return list.splice(args[0], args[1], args[2]);
-    return null;
+proto.list_functions.splice = function(list /*, ... args */ ) {
+    var args = Array.prototype.slice.call(arguments);
+    args.shift();
+    
+    return list.splice.apply(list,args);
 }
 
-proto.list_functions.push = function(list, args) {
-    list.push(args[0]);
+proto.list_functions.push = function(list, value) {
+    list.push(value);
     return list;
 }
 
@@ -598,8 +809,8 @@ proto.list_functions.pop = function(list) {
     return list.pop();
 }
 
-proto.list_functions.unshift = function(list, args) {
-    list.unshift(args[0]);
+proto.list_functions.unshift = function(list, value) {
+    list.unshift(value);
     return list;
 }
 
@@ -625,6 +836,11 @@ proto.list_functions.last = function(list) {
 
 proto.hash_functions = {};
 
+// typeof
+proto.hash_functions['typeof'] = function(hash) {
+    return 'object';
+};
+
 
 // each            list of alternating keys/values
 proto.hash_functions.each = function(hash) {
@@ -635,20 +851,17 @@ proto.hash_functions.each = function(hash) {
 }
 
 // exists(key)     does key exist?
-proto.hash_functions.exists = function(hash, args) {
-    return ( typeof( hash[args[0]] ) == "undefined" ) ? 0 : 1;
+proto.hash_functions.exists = function(hash, key) {
+    return ( typeof( hash[key] ) == "undefined" ) ? 0 : 1;
 }
 
-// FIXME proto.hash_functions.import blows everything up
-//
 // import(hash2)   import contents of hash2
 // import          import into current namespace hash
-//proto.hash_functions.import = function(hash, args) {
-//    var hash2 = args[0];
-//    for ( var key in hash2 )
-//        hash[key] = hash2[key];
-//    return '';
-//}
+proto.hash_functions['import'] = function(hash, hash2) {    
+    for ( var key in hash2 )
+        hash[key] = hash2[key];
+    return '';
+}
 
 // keys            list of keys
 proto.hash_functions.keys = function(hash) {
@@ -659,10 +872,10 @@ proto.hash_functions.keys = function(hash) {
 }
 
 // list            returns alternating key, value
-proto.hash_functions.list = function(hash, args) {
-    var what = '';
-    if ( args )
-        what = args[0];
+proto.hash_functions.list = function(hash, what) {
+    //var what = '';
+    //if ( args )
+        //what = args[0];
 
     var list = new Array();
     var key;
@@ -690,6 +903,11 @@ proto.hash_functions.nsort = function(hash) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
+// item           return a value by key
+proto.hash_functions.item = function(hash, key) {
+    return hash[key];
+}
+
 // size            number of pairs
 proto.hash_functions.size = function(hash) {
     var size = 0;
@@ -715,9 +933,23 @@ proto.hash_functions.values = function(hash) {
     return list;
 }
 
+proto.hash_functions.pairs = function(hash) {
+    var list = new Array();
+    var keys = new Array();
+    for ( var key in hash ) {
+        keys.push( key );
+    }
+    keys.sort();
+    for ( var key in keys ) {
+        key = keys[key]
+        list.push( { 'key': key, 'value': hash[key] } );
+    }
+    return list;
+}
+
 //  delete
-proto.hash_functions.remove = function(hash, args) {
-    return delete hash[args[0]];
+proto.hash_functions.remove = function(hash, key) {
+    return delete hash[key];
 }
 proto.hash_functions['delete'] = proto.hash_functions.remove;
 
@@ -740,6 +972,9 @@ if (typeof Jemplate.Iterator == 'undefined') {
             this.object_keys = object_keys.sort();
             this.size = object_keys.length;
             this.max  = this.size -1;
+        } else if (typeof object == 'undefined' || object == null || object == '') {
+            this.object = null;
+            this.max  = -1;
         }
     }
 }
@@ -776,7 +1011,7 @@ proto.get_next = function(should_init) {
             return [this.object_keys[index], false];
         }
     } else {
-        if (index < object.length) {
+        if (index <= this.max) {
             this.prev = index > 0 ? object[index - 1] : "";
             this.next = index < this.max ? object[index +1] : "";
             return [object[index], false];
@@ -818,8 +1053,8 @@ Jemplate.JSON = {
 }());
 
 ;/*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -827,44 +1062,48 @@ Jemplate.JSON = {
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
 
-            This is the toJSON method added to Dates:
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -872,35 +1111,24 @@ Jemplate.JSON = {
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -911,16 +1139,22 @@ Jemplate.JSON = {
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -940,45 +1174,57 @@ Jemplate.JSON = {
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -988,175 +1234,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -1164,123 +1414,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
+
 
+// If the JSON object does not yet have a parse method, give it one.
 
-            parse: function (text, reviver) {
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
 
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
 
-// We split the first stage into 4 regexp operations in order to work around
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 ;;(function(){
 
@@ -1,15 +1,21 @@
-use t::TestJemplate tests => 2;
+use t::TestJemplate tests => 3;
 
 filters {
+    'empty_template_body' => 'compile',
     'tt_to_full' => 'compile',
     'tt' => 'compile_lite',
 };
+run_is 'empty_template_body' => 'js';
 run_is 'tt_to_full' => 'js';
 run_is 'tt' => 'js';
 
 __END__
 
 ===
+--- empty_template_body
+--- js -trim
+Jemplate.templateMap['test_template'] = function() { return ''; }
+===
 --- tt_to_full
 Foo
 [% PROCESS red_meat -%]
@@ -25,7 +25,7 @@ if (typeof Jemplate == 'undefined') {
 Jemplate.VERSION = '0.22';
 
 Jemplate.process = function() {
-    var jemplate = new Jemplate();
+    var jemplate = new Jemplate(Jemplate.prototype.config);
     return jemplate.process.apply(jemplate, arguments);
 }
 
@@ -36,38 +36,83 @@ if (! Jemplate.templateMap)
 
 var proto = Jemplate.prototype = {};
 
+proto.config = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    FILTERS: {},
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+proto.defaults = {
+    AUTO_RESET: true,
+    BLOCKS: {},
+    CONTEXT: null,
+    DEBUG_UNDEF: false,
+    DEFAULT: null,
+    ERROR: null,
+    EVAL_JAVASCRIPT: false,
+    GLOBAL : true,
+	SCOPE : this,
+    INCLUDE_PATH: [''],
+    INTERPOLATE: false,
+    OUTPUT: null,
+    PLUGINS: {},
+    POST_PROCESS: [],
+    PRE_PROCESS: [],
+    PROCESS: null,
+    RECURSION: false,
+    STASH: null,
+    TOLERANT: null,
+    VARIABLES: {},
+    WRAPPER: []
+};
+
+
+Jemplate.init = function(config) {
+ 
+    Jemplate.prototype.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof Jemplate.prototype.config[i] == "undefined") {
+            Jemplate.prototype.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
+}
+
 proto.init = function(config) {
-    this.config = config ||
-    {
-        AUTO_RESET: true,
-        BLOCKS: {},
-        CONTEXT: null,
-        DEBUG_UNDEF: false,
-        DEFAULT: null,
-        ERROR: null,
-        EVAL_JAVASCRIPT: false,
-        FILTERS: {},
-        INCLUDE_PATH: [''],
-        INTERPOLATE: false,
-        OUTPUT: null,
-        PLUGINS: {},
-        POST_PROCESS: [],
-        PRE_PROCESS: [],
-        PROCESS: null,
-        RECURSION: false,
-        STASH: null,
-        TOLERANT: null,
-        VARIABLES: {},
-        WRAPPER: []
-    };
+    
+    this.config = config || {};
+    
+    for (var i in Jemplate.prototype.defaults) {
+        if(typeof this.config[i] == "undefined") {
+            this.config[i] = Jemplate.prototype.defaults[i];
+        }
+    }
 }
 
 proto.process = function(template, data, output) {
     var context = this.config.CONTEXT || new Jemplate.Context();
     context.config = this.config;
 
-    context.stash = this.config.STASH || new Jemplate.Stash();
-    context.stash.__config__ = this.config;
+    context.stash = new Jemplate.Stash(this.config.STASH, this.config);
 
     context.__filter__ = new Jemplate.Filter();
     context.__filter__.config = this.config;
@@ -79,7 +124,17 @@ proto.process = function(template, data, output) {
 
     var proc = function(input) {
         try {
+            if (typeof context.config.PRE_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.PRE_PROCESS];                
+            for (var i = 0; i < context.config.PRE_PROCESS.length; i++) {
+                context.process(context.config.PRE_PROCESS[i]);
+            }
+            
             result = context.process(template, input);
+            
+            if (typeof context.config.POST_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.POST_PROCESS];
+            for (i = 0; i < context.config.POST_PROCESS.length; i++) {
+                context.process(context.config.POST_PROCESS[i]);
+            }
         }
         catch(e) {
             if (! String(e).match(/Jemplate\.STOP\n/))
@@ -161,7 +216,8 @@ proto.plugin = function(name, args) {
         throw "Unknown plugin name ':" + name + "'";
 
     // The Context object (this) is passed as the first argument to the plugin.
-    return new window[name](this, args);
+	var func = eval(name);
+    return new func(this, args);
 }
 
 proto.filter = function(text, name, args) {
@@ -315,8 +371,16 @@ proto.filters.replace = function(text, args) {
 // Jemplate.Stash class
 //------------------------------------------------------------------------------
 if (typeof Jemplate.Stash == 'undefined') {
-    Jemplate.Stash = function() {
-        this.data = {};
+    Jemplate.Stash = function(stash, config) {
+        this.__config__ = config;
+		
+		this.data = {
+			GLOBAL : this.__config__.SCOPE			
+		};
+		this.LOCAL_ANCHOR = {};
+		this.data.LOCAL = this.LOCAL_ANCHOR;
+		
+		this.update(stash);
     };
 }
 
@@ -324,7 +388,10 @@ proto = Jemplate.Stash.prototype;
 
 proto.clone = function(args) {
     var data = this.data;
-    this.data = {};
+    this.data = {
+		GLOBAL : this.__config__.SCOPE
+	};
+	this.data.LOCAL = this.LOCAL_ANCHOR;
     this.update(data);
     this.update(args);
     this.data._PARENT = data;
@@ -337,87 +404,221 @@ proto.declone = function(args) {
 proto.update = function(args) {
     if (typeof args == 'undefined') return;
     for (var key in args) {
-        var value = args[key];
-        this.set(key, value);
+        if (key != 'GLOBAL' && key != 'LOCAL') {
+	        this.set(key, args[key]);
+		}
     }
 }
 
-proto.get = function(key) {
+proto.get = function(ident, args) {
     var root = this.data;
-    if (key instanceof Array) {
-        for (var i = 0; i < key.length; i += 2) {
-            var args = key.slice(i, i+2);
-            args.unshift(root);
-            value = this._dotop.apply(this, args);
+    
+    var value;
+    
+    if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+        
+        if (typeof ident == 'string') {
+            ident = ident.split('.');
+            var newIdent = [];
+            for (var i = 0; i < ident.length; i++) {
+                newIdent.push(ident.replace(/\(.*$/,''));
+                newIdent.push(0);
+            }
+            ident = newIdent;
+        }
+        
+        for (var i = 0; i < ident.length; i += 2) {
+            var dotopArgs = ident.slice(i, i+2);
+            dotopArgs.unshift(root);
+            value = this._dotop.apply(this, dotopArgs);
             if (typeof value == 'undefined')
                 break;
             root = value;
         }
     }
     else {
-        value = this._dotop(root, key);
+        value = this._dotop(root, ident, args);
     }
 
-    if (typeof value == 'undefined') {
+    if (typeof value == 'undefined' || value == null) {
         if (this.__config__.DEBUG_UNDEF)
-            throw("undefined value found while using DEGUG_UNDEF");
+            throw("undefined value found while using DEBUG_UNDEF");
         value = '';
     }
 
     return value;
 }
 
-proto.set = function(key, value, set_default) {
-    if (key instanceof Array) {
-        var data = this.get(key[0]) || {};
-        key = key[2];
-    }
-    else {
-        data = this.data;
+
+
+proto.set = function(ident, value, set_default) {
+    
+    var root, result, error;
+    
+    root = this.data;
+    
+    while (true) {
+        if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) {
+            
+            if (typeof ident == 'string') {
+                ident = ident.split('.');
+                var newIdent = [];
+                for (var i = 0; i < ident.length; i++) {
+                    newIdent.push(ident.replace(/\(.*$/,''));
+                    newIdent.push(0);
+                }
+                ident = newIdent;
+            }
+            
+            for (var i = 0; i < ident.length - 2; i += 2) {
+                var dotopArgs = ident.slice(i, i+2);
+                dotopArgs.unshift(root);
+                dotopArgs.push(1);
+                result = this._dotop.apply(this, dotopArgs);
+                if (typeof value == 'undefined')
+                    break;
+                root = result;
+            }
+            
+            var assignArgs = ident.slice(ident.length-2, ident.length);
+            assignArgs.unshift(root);
+            assignArgs.push(value);
+            assignArgs.push(set_default);
+            
+            
+            result = this._assign.apply(this, assignArgs);
+        } else {
+            result = this._assign(root, ident, 0, value, set_default);
+        }
+        break;
     }
-    if (! (set_default && (typeof data[key] != 'undefined')))
-        data[key] = value;
+    
+    return (typeof result != 'undefined') ? result : '';
 }
 
-proto._dotop = function(root, item, args) {
-    if (typeof item == 'undefined' ||
-        typeof item == 'string' && item.match(/^[\._]/)) {
+
+
+proto._dotop = function(root, item, args, lvalue) {    
+    if (root == this.LOCAL_ANCHOR) root = this.data;
+	var atroot = root == this.data;
+    
+    var value,result = undefined;
+    
+   	var is_function_call = args instanceof Array;
+   	
+   	args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
         return undefined;
     }
 
-    if ((! args) &&
-        (typeof root == 'object') &&
-        (!(root instanceof Array) || (typeof item == 'number')) &&
-        (typeof root[item] != 'undefined')) {
-        var value = root[item];
-        if (typeof value == 'function')
-            value = value.apply(root);
-        return value;
+
+    //root is complex object, not scalar
+    if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) {
+        
+		if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null
+            if (typeof root[item] == 'function') {
+                result = root[item].apply(root,args);
+            } else {
+                return root[item];
+            }
+        } else if (lvalue) {
+            return root[item] = {};
+        } else if (this.hash_functions[item] && !atroot || item == 'import') {
+            args.unshift(root);
+            result = this.hash_functions[item].apply(this,args);
+        } else if (item instanceof Array) {
+            result = {};
+            
+            for (var i = 0; i < item.length; i++) result[item[i]] = root[item[i]];
+            return result;
+        }
+    } else if (root instanceof Array) {
+        if (this.list_functions[item]) {
+            args.unshift(root);
+            result = this.list_functions[item].apply(this,args);
+        } else if (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' ) {
+            if (typeof root[item] != 'function') return root[item];
+            result = root[item].apply(this, args);
+        } else if (item instanceof Array) {
+            for (var i = 0; i < item.length; i++) result.push(root[item[i]]);
+            return result;
+        }
+    } else if (this.string_functions[item] && !lvalue) {
+        args.unshift(root);
+        result = this.string_functions[item].apply(this, args);
+    } else if (this.list_functions[item] && !lvalue) {
+        args.unshift([root]);
+        result = this.list_functions[item].apply(this,args);
+    } else {
+        result = undefined;
     }
+    
+    
+    if (result instanceof Array) {
+		if (typeof result[0] == 'undefined' && typeof result[1] != 'undefined') {
+	        throw result[1];
+	    }
+	}
+    
+    return result;
+
+}
 
-    if (typeof root == 'string' && this.string_functions[item])
-        return this.string_functions[item](root, args);
-    if (root instanceof Array && this.list_functions[item])
-        return this.list_functions[item](root, args);
-    if (typeof root == 'object' && this.hash_functions[item])
-        return this.hash_functions[item](root, args);
-    if (typeof root[item] == 'function')
-        return root[item].apply(root, args);
 
+proto._assign = function(root, item, args, value, set_default) {
+    var atroot = root == this.data;
+    var result;
+    
+    args = args || [];
+    
+    if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) {
+        return undefined;
+    }
+    
+    if (atroot || root.constructor == Object || root == this.data.GLOBAL) {
+		
+		if (root == this.LOCAL_ANCHOR) root = this.data;
+			 
+		if (!(set_default && typeof root[item] != 'undefined')) {
+            if (atroot && item == 'GLOBAL') throw "Attempt to modify GLOBAL access modifier"
+			if (atroot && item == 'LOCAL') throw "Attempt to modify LOCAL access modifier"
+			
+			return root[item] = value;
+        } 
+    } else if ((root instanceof Array) && (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' )) {
+        if (!(set_default && typeof root[item] != 'undefined')) {
+            return root[item] = value;
+        }
+    } else if ( (root.constructor != Object) && (root instanceof Object) ) {
+        try {
+            result = root[item].apply(root,args);
+        } catch (e) {
+        }
+    } else {
+        throw 'dont know how to assign to [' + root + '.' + item +']';
+    }
+    
     return undefined;
 }
 
+
 proto.string_functions = {};
 
+// typeof
+proto.string_functions['typeof'] = function(value) {
+    return typeof value;
+}
+
 // chunk(size)     negative size chunks from end
-proto.string_functions.chunk = function(string, args) {
-    var size = args[0];
+proto.string_functions.chunk = function(string, size) {
+    //var size = args;
     var list = new Array();
     if (! size)
         size = 1;
     if (size < 0) {
         size = 0 - size;
-        for (i = string.length - size; i >= 0; i = i - size)
+        for (var i = string.length - size; i >= 0; i = i - size)
             list.unshift(string.substr(i, size));
         if (string.length % size)
             list.unshift(string.substr(0, string.length % size));
@@ -449,15 +650,15 @@ proto.string_functions.list = function(string) {
 }
 
 // match(re)       get list of matches
-proto.string_functions.match = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
+proto.string_functions.match = function(string, re, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);
     var list = string.match(regexp);
     return list;
 }
 
 // repeat(n)       repeated n times
 proto.string_functions.repeat = function(string, args) {
-    var n = args[0] || 1;
+    var n = args || 1;
     var output = '';
     for (var i = 0; i < n; i++) {
         output += string;
@@ -465,19 +666,17 @@ proto.string_functions.repeat = function(string, args) {
     return output;
 }
 
-// replace(re, sub)    replace instances of re with sub
-proto.string_functions.replace = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var sub = args[1];
-    if (! sub)
-        sub  = '';
-    var output = string.replace(regexp, sub);
-    return output;
+// replace(re, sub, global)    replace instances of re with sub
+proto.string_functions.replace = function(string, re, sub, modifiers) {
+    var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers);    
+    if (! sub) sub  = '';
+
+    return string.replace(regexp, sub);
 }
 
 // search(re)      true if value matches re
-proto.string_functions.search = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.search = function(string, re) {
+    var regexp = new RegExp(re);
     return (string.search(regexp) >= 0) ? 1 : 0;
 }
 
@@ -487,8 +686,8 @@ proto.string_functions.size = function(string) {
 }
 
 // split(re)       split string on re
-proto.string_functions.split = function(string, args) {
-    var regexp = new RegExp(args[0]);
+proto.string_functions.split = function(string, re) {
+    var regexp = new RegExp(re);
     var list = string.split(regexp);
     return list;
 }
@@ -497,8 +696,18 @@ proto.string_functions.split = function(string, args) {
 
 proto.list_functions = {};
 
-proto.list_functions.join = function(list, args) {
-    return list.join(args[0]);
+// typeof
+proto.list_functions['typeof'] = function(list) {
+    return 'array';
+};
+
+
+proto.list_functions.list = function(list) {
+    return list;
+};
+
+proto.list_functions.join = function(list, str) {
+    return list.join(str);
 };
 
 proto.list_functions.sort = function(list,key) {
@@ -526,8 +735,8 @@ proto.list_functions.nsort = function(list) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
-proto.list_functions.grep = function(list, args) {
-    var regexp = new RegExp(args[0]);
+proto.list_functions.grep = function(list, re) {
+    var regexp = new RegExp(re);
     var result = [];
     for (var i = 0; i < list.length; i++) {
         if (list[i].match(regexp))
@@ -556,7 +765,7 @@ proto.list_functions.reverse = function(list) {
     return result;
 }
 
-proto.list_functions.merge = function(list, args) {
+proto.list_functions.merge = function(list /*, ... args */) {
     var result = [];
     var push_all = function(elem) {
         if (elem instanceof Array) {
@@ -569,28 +778,30 @@ proto.list_functions.merge = function(list, args) {
         }
     }
     push_all(list);
-    for (var i = 0; i < args.length; i++) {
-        push_all(args[i]);
+    for (var i = 1; i < arguments.length; i++) {
+        push_all(arguments[i]);
     }
     return result;
 }
 
-proto.list_functions.slice = function(list, args) {
-    return list.slice(args[0], args[1]);
+proto.list_functions.slice = function(list, start, end) {
+    // To make it like slice in TT
+    // See rt53453
+    if ( end == -1 ) {
+        return list.slice( start );
+    }
+    return list.slice( start, end + 1 );
 }
 
-proto.list_functions.splice = function(list, args) {
-    if (args.length == 1)
-        return list.splice(args[0]);
-    if (args.length == 2)
-        return list.splice(args[0], args[1]);
-    if (args.length == 3)
-        return list.splice(args[0], args[1], args[2]);
-    return null;
+proto.list_functions.splice = function(list /*, ... args */ ) {
+    var args = Array.prototype.slice.call(arguments);
+    args.shift();
+    
+    return list.splice.apply(list,args);
 }
 
-proto.list_functions.push = function(list, args) {
-    list.push(args[0]);
+proto.list_functions.push = function(list, value) {
+    list.push(value);
     return list;
 }
 
@@ -598,8 +809,8 @@ proto.list_functions.pop = function(list) {
     return list.pop();
 }
 
-proto.list_functions.unshift = function(list, args) {
-    list.unshift(args[0]);
+proto.list_functions.unshift = function(list, value) {
+    list.unshift(value);
     return list;
 }
 
@@ -625,6 +836,11 @@ proto.list_functions.last = function(list) {
 
 proto.hash_functions = {};
 
+// typeof
+proto.hash_functions['typeof'] = function(hash) {
+    return 'object';
+};
+
 
 // each            list of alternating keys/values
 proto.hash_functions.each = function(hash) {
@@ -635,20 +851,17 @@ proto.hash_functions.each = function(hash) {
 }
 
 // exists(key)     does key exist?
-proto.hash_functions.exists = function(hash, args) {
-    return ( typeof( hash[args[0]] ) == "undefined" ) ? 0 : 1;
+proto.hash_functions.exists = function(hash, key) {
+    return ( typeof( hash[key] ) == "undefined" ) ? 0 : 1;
 }
 
-// FIXME proto.hash_functions.import blows everything up
-//
 // import(hash2)   import contents of hash2
 // import          import into current namespace hash
-//proto.hash_functions.import = function(hash, args) {
-//    var hash2 = args[0];
-//    for ( var key in hash2 )
-//        hash[key] = hash2[key];
-//    return '';
-//}
+proto.hash_functions['import'] = function(hash, hash2) {    
+    for ( var key in hash2 )
+        hash[key] = hash2[key];
+    return '';
+}
 
 // keys            list of keys
 proto.hash_functions.keys = function(hash) {
@@ -659,10 +872,10 @@ proto.hash_functions.keys = function(hash) {
 }
 
 // list            returns alternating key, value
-proto.hash_functions.list = function(hash, args) {
-    var what = '';
-    if ( args )
-        what = args[0];
+proto.hash_functions.list = function(hash, what) {
+    //var what = '';
+    //if ( args )
+        //what = args[0];
 
     var list = new Array();
     var key;
@@ -690,6 +903,11 @@ proto.hash_functions.nsort = function(hash) {
     return list.sort(function(a, b) { return (a-b) });
 }
 
+// item           return a value by key
+proto.hash_functions.item = function(hash, key) {
+    return hash[key];
+}
+
 // size            number of pairs
 proto.hash_functions.size = function(hash) {
     var size = 0;
@@ -715,9 +933,23 @@ proto.hash_functions.values = function(hash) {
     return list;
 }
 
+proto.hash_functions.pairs = function(hash) {
+    var list = new Array();
+    var keys = new Array();
+    for ( var key in hash ) {
+        keys.push( key );
+    }
+    keys.sort();
+    for ( var key in keys ) {
+        key = keys[key]
+        list.push( { 'key': key, 'value': hash[key] } );
+    }
+    return list;
+}
+
 //  delete
-proto.hash_functions.remove = function(hash, args) {
-    return delete hash[args[0]];
+proto.hash_functions.remove = function(hash, key) {
+    return delete hash[key];
 }
 proto.hash_functions['delete'] = proto.hash_functions.remove;
 
@@ -740,6 +972,9 @@ if (typeof Jemplate.Iterator == 'undefined') {
             this.object_keys = object_keys.sort();
             this.size = object_keys.length;
             this.max  = this.size -1;
+        } else if (typeof object == 'undefined' || object == null || object == '') {
+            this.object = null;
+            this.max  = -1;
         }
     }
 }
@@ -776,7 +1011,7 @@ proto.get_next = function(should_init) {
             return [this.object_keys[index], false];
         }
     } else {
-        if (index < object.length) {
+        if (index <= this.max) {
             this.prev = index > 0 ? object[index - 1] : "";
             this.next = index < this.max ? object[index +1] : "";
             return [object[index], false];
@@ -818,8 +1053,8 @@ Jemplate.JSON = {
 }());
 
 ;/*
-    json2.js
-    2008-03-24
+    http://www.JSON.org/json2.js
+    2009-04-16
 
     Public Domain.
 
@@ -827,44 +1062,48 @@ Jemplate.JSON = {
 
     See http://www.JSON.org/js.html
 
-    This file creates a global JSON object containing three methods: stringify,
-    parse, and quote.
-
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
 
         JSON.stringify(value, replacer, space)
             value       any JavaScript value, usually an object or array.
 
             replacer    an optional parameter that determines how object
-                        values are stringified for objects without a toJSON
-                        method. It can be a function or an array.
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
 
             space       an optional parameter that specifies the indentation
                         of nested structures. If it is omitted, the text will
                         be packed without extra whitespace. If it is a number,
                         it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t'), it contains the
-                        characters used to indent at each level.
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
 
             This method produces a JSON text from a JavaScript value.
 
             When an object value is found, if the object contains a toJSON
-            method, its toJSON method with be called and the result will be
+            method, its toJSON method will be called and the result will be
             stringified. A toJSON method does not serialize: it returns the
             value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method will
-            be passed the key associated with the value, and this will be bound
-            to the object holding the key.
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
 
-            This is the toJSON method added to Dates:
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
 
-                function toJSON(key) {
                     return this.getUTCFullYear()   + '-' +
                          f(this.getUTCMonth() + 1) + '-' +
                          f(this.getUTCDate())      + 'T' +
                          f(this.getUTCHours())     + ':' +
                          f(this.getUTCMinutes())   + ':' +
                          f(this.getUTCSeconds())   + 'Z';
-                }
+                };
 
             You can provide an optional replacer method. It will be passed the
             key and value of each member, with this bound to the containing
@@ -872,35 +1111,24 @@ Jemplate.JSON = {
             serialized. If your method returns undefined, then the member will
             be excluded from the serialization.
 
-            If no replacer parameter is provided, then a default replacer
-            will be used:
-
-                function replacer(key, value) {
-                    return Object.hasOwnProperty.call(this, key) ?
-                        value : undefined;
-                }
-
-            The default replacer is passed the key and value for each item in
-            the structure. It excludes inherited members.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
             stringified.
 
-            Values that do not have JSON representaions, such as undefined or
+            Values that do not have JSON representations, such as undefined or
             functions, will not be serialized. Such values in objects will be
             dropped; in arrays they will be replaced with null. You can use
             a replacer function to replace those with JSON values.
             JSON.stringify(undefined) returns undefined.
 
-            The optional space parameter produces a stringification of the value
-            that is filled with line breaks and indentation to make it easier to
-            read.
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
 
             If the space parameter is a non-empty string, then that string will
             be used for indentation. If the space parameter is a number, then
-            then indentation will be that many spaces.
+            the indentation will be that many spaces.
 
             Example:
 
@@ -911,16 +1139,22 @@ Jemplate.JSON = {
             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
 
         JSON.parse(text, reviver)
             This method parses a JSON text to produce an object or array.
             It can throw a SyntaxError exception.
 
             The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values, and
-            its return value is used instead of the original value. If it
-            returns what it received, then structure is not modified. If it
-            returns undefined then the member is deleted.
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
 
             Example:
 
@@ -940,45 +1174,57 @@ Jemplate.JSON = {
                 return value;
             });
 
-
-        JSON.quote(text)
-            This method wraps a string in quotes, escaping some characters
-            as needed.
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
 
 
     This is a reference implementation. You are free to copy, modify, or
     redistribute.
 
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
-    CODE INTO YOUR PAGES.
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
 */
 
-/*jslint regexp: true, forin: true, evil: true */
+/*jslint evil: true */
 
 /*global JSON */
 
 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
-    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
-    test, toJSON, toString
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
 */
 
-if (!JSON) var JSON;
-if (!JSON) {
-
 // Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid global variables.
+// methods in a closure to avoid creating global variables.
 
-    JSON = function () {
+if (!this.JSON) {
+    this.JSON = {};
+}
+(function () {
 
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
 
-        Date.prototype.toJSON = function () {
+    if (typeof Date.prototype.toJSON !== 'function') {
 
-// Eventually, this method will be based on the date.toISOString method.
+        Date.prototype.toJSON = function (key) {
 
             return this.getUTCFullYear()   + '-' +
                  f(this.getUTCMonth() + 1) + '-' +
@@ -988,175 +1234,179 @@ if (!JSON) {
                  f(this.getUTCSeconds())   + 'Z';
         };
 
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
 
-        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
 
 
-        function quote(string) {
+    function quote(string) {
 
 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.
 
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    c = a.charCodeAt();
-                    return '\\u00' + Math.floor(c / 16).toString(16) +
-                                               (c % 16).toString(16);
-                }) + '"' :
-                '"' + string + '"';
-        }
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
 
 
-        function str(key, holder) {
+    function str(key, holder) {
 
 // Produce a string from holder[key].
 
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
 
 // If the value has a toJSON method, call it to obtain a replacement value.
 
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
 
 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.
 
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
 
 // What happens next depends on the value's type.
 
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
 
-            case 'number':
+        case 'number':
 
 // JSON numbers must be finite. Encode non-finite numbers as null.
 
-                return isFinite(value) ? String(value) : 'null';
+            return isFinite(value) ? String(value) : 'null';
 
-            case 'boolean':
-            case 'null':
+        case 'boolean':
+        case 'null':
 
 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce 'null'. The case is included here in
 // the remote chance that this gets fixed someday.
 
-                return String(value);
+            return String(value);
 
 // If the type is 'object', we might be dealing with an object or an array or
 // null.
 
-            case 'object':
+        case 'object':
 
 // Due to a specification blunder in ECMAScript, typeof null is 'object',
 // so watch out for that case.
 
-                if (!value) {
-                    return 'null';
-                }
+            if (!value) {
+                return 'null';
+            }
 
 // Make an array to hold the partial results of stringifying this object value.
 
-                gap += indent;
-                partial = [];
+            gap += indent;
+            partial = [];
 
-// If the object has a dontEnum length property, we'll treat it as an array.
+// Is the value an array?
 
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
-// The object is an array. Stringify every element. Use null as a placeholder
+// The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.
 
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
 
 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.
 
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
-                                  '\n' + mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
 
 // If the replacer is an array, use it to select the members to be stringified.
 
-                if (typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value, rep);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
-                } else {
+                }
+            } else {
 
 // Otherwise, iterate through all of the keys in the object.
 
-                    for (k in value) {
-                        v = str(k, value, rep);
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
                         }
                     }
                 }
+            }
 
 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.
 
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
-                              '\n' + mind + '}' :
-                          '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-
-            return null;
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
         }
 
+        return '';
+    }
 
-// Return the JSON object containing the stringify, parse, and quote methods.
+// If the JSON object does not yet have a stringify method, give it one.
 
-        return {
-            stringify: function (value, replacer, space) {
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
 
 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
@@ -1164,123 +1414,124 @@ if (!JSON) {
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.
 
-                var i;
-                gap = '';
-                indent = '';
-                if (space) {
+            var i;
+            gap = '';
+            indent = '';
 
 // If the space parameter is a number, make an indent string containing that
 // many spaces.
 
-                    if (typeof space === 'number') {
-                        for (i = 0; i < space; i += 1) {
-                            indent += ' ';
-                        }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                    } else if (typeof space === 'string') {
-                        indent = space;
-                    }
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
                 }
 
-// If there is no replacer parameter, use the default replacer.
+// If the space parameter is a string, it will be used as the indent string.
 
-                if (!replacer) {
-                    rep = function (key, value) {
-                        if (!Object.hasOwnProperty.call(this, key)) {
-                            return undefined;
-                        }
-                        return value;
-                    };
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
 
-// The replacer can be a function or an array. Otherwise, throw an error.
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
 
-                } else if (typeof replacer === 'function' ||
-                        (typeof replacer === 'object' &&
-                         typeof replacer.length === 'number')) {
-                    rep = replacer;
-                } else {
-                    throw new Error('JSON.stringify');
-                }
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
 
 // Make a fake root object containing our value under the key of ''.
 // Return the result of stringifying the value.
 
-                return str('', {'': value});
-            },
+            return str('', {'': value});
+        };
+    }
+
 
+// If the JSON object does not yet have a parse method, give it one.
 
-            parse: function (text, reviver) {
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
 
 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.
 
-                var j;
+            var j;
 
-                function walk(holder, key) {
+            function walk(holder, key) {
 
 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.
 
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
                             }
                         }
                     }
-                    return reviver.call(holder, key, value);
                 }
+                return reviver.call(holder, key, value);
+            }
 
 
-// Parsing happens in three stages. In the first stage, we run the text against
-// regular expressions that look for non-JSON patterns. We are especially
-// concerned with '()' and 'new' because they can cause invocation, and '='
-// because it can cause mutation. But just to be safe, we want to reject all
-// unexpected forms.
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
 
-// We split the first stage into 4 regexp operations in order to work around
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
 // replace all simple value tokens with ']' characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or ']' or
 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
 
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
-// In the second stage we use the eval function to compile the text into a
+// In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.
 
-                    j = eval('(' + text + ')');
+                j = eval('(' + text + ')');
 
-// In the optional third stage, we recursively walk the new structure, passing
+// In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.
 
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
 
 // If the text is not JSON parseable, then a SyntaxError is thrown.
 
-                throw new SyntaxError('JSON.parse');
-            },
-
-            quote: quote
+            throw new SyntaxError('JSON.parse');
         };
-    }();
-}
+    }
+}());
 
 ;;(function(){
 
@@ -1656,12 +1907,45 @@ Jemplate.JSON = {
    Template Toolkit. Any changes made to this file will be lost the next
    time the templates are compiled.
 
-   Copyright 2006 - Ingy döt Net - All rights reserved.
+   Copyright 2006-2008 - Ingy döt Net - All rights reserved.
 */
 
 if (typeof(Jemplate) == 'undefined')
     throw('Jemplate.js must be loaded before any Jemplate template files');
 
+Jemplate.templateMap['a/b/cherry'] = function(context) {
+    if (! context) throw('Jemplate function called without context\n');
+    var stash = context.stash;
+    var output = '';
+
+    try {
+
+output += '\n';
+    }
+    catch(e) {
+        var error = context.set_error(e, output);
+        throw(error);
+    }
+
+    return output;
+}
+
+Jemplate.templateMap['cherry'] = function(context) {
+    if (! context) throw('Jemplate function called without context\n');
+    var stash = context.stash;
+    var output = '';
+
+    try {
+output += '\napple\n';
+    }
+    catch(e) {
+        var error = context.set_error(e, output);
+        throw(error);
+    }
+
+    return output;
+}
+
 Jemplate.templateMap['hello'] = function(context) {
     if (! context) throw('Jemplate function called without context\n');
     var stash = context.stash;
@@ -0,0 +1,56 @@
+use t::TestJemplate tests => 3;
+
+filters {
+    'macro_definition' => 'compile_lite',
+    'macro_call' => 'compile_lite',
+    'macro_call_with_complex_arguments' => 'compile_lite',
+};
+run_is 'macro_definition' => 'js';
+run_is 'macro_call' => 'js';
+run_is 'macro_call_with_complex_arguments' => 'js';
+
+__END__
+
+===
+--- macro_definition
+[% 
+	MACRO simple(param1,param2) BLOCK;
+		param1 _ param2;
+	END;
+%]
+--- js -trim
+//line 1 "test_template"
+
+//MACRO
+stash.set('simple', function () {
+    var output = '';
+    var args = {};
+    var fargs = Array.prototype.slice.call(arguments);
+    args['param1'] = fargs.shift();args['param2'] = fargs.shift();
+    args.arguments = Array.prototype.slice.call(arguments);
+
+    var params = fargs.shift() || {};
+
+    for (var key in params) {
+        args[key] = params[key];
+    }
+
+    context.stash.clone(args);
+    try {
+//line 1 "test_template"
+output += stash.get('param1')  + stash.get('param2');
+===
+--- macro_call
+[% simple('1','2') %]
+--- js -trim
+//line 1 "test_template"
+output += stash.get(['simple', [ '1', '2' ]]);
+output += '\n';
+===
+--- macro_call_with_complex_arguments
+[% simple('1' _ '2','3') %]
+
+--- js
+//line 1 "test_template"
+output += stash.get(['simple', [ '1'  + '2', '3' ]]);
+output += '\n';
@@ -0,0 +1,38 @@
+use Test::More;
+
+BEGIN {
+    plan skip_all => "JavaScript::V8x::TestMoreish not available" unless eval { require JavaScript::V8x::TestMoreish };
+}
+
+plan qw/no_plan/;
+
+use Jemplate;
+use Jemplate::Runtime;
+
+use JavaScript::V8x::TestMoreish;
+
+my $jemplate = Jemplate->new;
+my @templates;
+push @templates, $jemplate->compile_template_content( <<_END_, 't0' );
+Hello, World.
+_END_
+push @templates, $jemplate->compile_template_content( <<_END_, 't1' );
+[% FOREACH pair = hash.pairs %]
+[% pair.key %] = [% pair.value %]
+[% END %]
+_END_
+push @templates, $jemplate->compile_template_content( <<_END_, 't2' );
+[% FOREACH key = hash.keys %]
+[% key %] = [% hash.\$key %]
+[% END %]
+_END_
+
+test_js_eval( Jemplate::Runtime->kernel );
+test_js_eval( join "\n", @templates, "1;" );
+
+test_js <<'_END_';
+result = Jemplate.process( 't1', { hash: { c: 1, a: 2, b: 3 } } );
+like( result, /a = 2\s+b = 3\s+c = 1/ )
+_END_
+
+1;
@@ -17,7 +17,7 @@ stash.set('foo', 'foo');
 //line 1 "test_template"
 stash.set('bar', 'bar');
 output += '\n';
-//line 0 "test_template"
+//line 1 "test_template"
 output += stash.get('foo') + '/' + stash.get('bar');
 output += '\n';
 
@@ -1,16 +1,72 @@
-.PHONY: deps run-files
+.PHONY: all clean purge
 
-INGY_JSAN_CODE=../../../ingy/jsan
+RENDERER:=$(shell echo `js-cpan Test/Base/bin/render-template`)
+TEST_TEMPLATE:=$(shell echo `js-cpan Test/Base/template/test.html`)
+INDEX_TEMPLATE:=$(shell echo `js-cpan Test/Base/template/index.html`)
+TEST_LOCAL=template/test.html
+INDEX_LOCAL=template/index.html
 
-all: run-files
+MD5SUM=perl -MDigest::MD5 -e \
+    'open X,shift;$$d=Digest::MD5->new;$$d->addfile(*X);print $$d->hexdigest'
 
-deps: run
-	cp -r $(INGY_JSAN_CODE)/Test-Base/sample/run/lib/ run/lib/
-	cp -r $(INGY_JSAN_CODE)/Test-Base/sample/run/bin/ run/bin/
-	cp -r $(INGY_JSAN_CODE)/Test-Base/sample/run/template/ run/template/
+TEST_TEMPLATE_MD5=$(word 1,$(shell $(MD5SUM) $(TEST_TEMPLATE)))
+TEST_LOCAL_MD5=$(word 1,$(shell $(MD5SUM) $(TEST_LOCAL)))
+INDEX_TEMPLATE_MD5=$(word 1,$(shell $(MD5SUM) $(INDEX_TEMPLATE)))
+INDEX_LOCAL_MD5=$(word 1,$(shell $(MD5SUM) $(INDEX_LOCAL)))
 
-run:
-	mkdir $@
+ALL_T_HTML:=$(shell ls -1 t/*.t.js | perl -pe 's/js$$/html/;s/^t\///')
 
-run-files:
-	make -C run clean all
+ALL_DIRS:=lib template
+
+ALL_TARGETS:=$(ALL_T_HTML) index.html
+
+ALL_LIBS= \
+	lib/Test/Base.js \
+	lib/Test/Builder.js \
+	lib/Test/Harness.js \
+	lib/Test/Harness/Browser.js \
+
+all: $(ALL_DIRS) $(ALL_TARGETS) $(ALL_LIBS)
+
+clean:
+	rm -fr $(ALL_TARGETS) $(ALL_LIBS)
+	if [ "$(TEST_TEMPLATE_MD5)" = \
+     "$(TEST_LOCAL_MD5)" ]; then rm -f $(TEST_LOCAL); fi
+	if [ "$(INDEX_TEMPLATE_MD5)" = \
+     "$(INDEX_LOCAL_MD5)" ]; then rm -f $(INDEX_LOCAL); fi
+	-find $(ALL_DIRS) -depth -type d | xargs rmdir 2> /dev/null
+
+
+purge: clean
+	rm Makefile config.yaml
+
+%.t.html: $(TEST_LOCAL)
+	perl $(RENDERER) $(notdir $<) $(@:%.t.html=t/%.t.js) > $@
+
+index.html: $(INDEX_LOCAL) t/*.t.js
+	perl $(RENDERER) $(notdir $<) $(@:%.t.html=t/%.t.js) > $@
+
+lib/Test/Base.js: lib/Test
+	cp -f `js-cpan Test.Base` $@
+# 	ln -s ../../../lib/Test/Base.js $@
+
+lib/Test/Builder.js: lib/Test
+	cp -f `js-cpan Test.Builder` $@
+
+lib/Test/Harness.js: lib/Test
+	cp -f `js-cpan Test.Harness` $@
+
+lib/Test/Harness/Browser.js: lib/Test/Harness
+	cp -f `js-cpan Test.Harness.Browser` $@
+
+lib lib/Test lib/Test/Harness:
+	mkdir -p $@
+
+$(TEST_LOCAL):
+	cp -f $(TEST_TEMPLATE) $@
+
+$(INDEX_LOCAL):
+	cp -f $(INDEX_TEMPLATE) $@
+
+template:
+	mkdir -p $@
@@ -0,0 +1,54 @@
+This is the JavaScript testing area for Jemplate. If you are new to
+testing Jemplate, please read this.
+
+== RUNNING THE TESTS ==
+
+There are currently two ways to run these tests. 
+
+1) If you have a webserver pointing at this directory you can just go to
+   that URL and all the tests will run in your browser.
+
+2) You can do the following commands:
+
+        cd tjs
+        make clean test
+
+   The output will tell you a URL to load in your browser to run
+   the tests.
+
+== ADDING OR CHANGING TESTS ==
+
+This directory contains a bunch of files that look like this:
+
+    t/basic.t.html
+
+All the tests reside in the t/ directory. They have names like:
+
+    t/basic.t.js
+
+Each html file runs the corresponding js test. The file index.html runs
+all the tests. All of the html files are generated. To regenerate all
+the files, run this command:
+
+    make clean all
+
+All these generated HTML files are checked into the Jemplate repository.
+This is so that they will run in place on CPAN or JSAN. (Yes, you can
+actually run the Jemplate tests directly from CPAN and JSAN.)
+
+== NOTES ON TJS ==
+
+The tjs/ directory gives you more control for running tests. You can
+build/test different runtimes with commands like:
+
+    make test
+    make test-jquery
+    make test-yui
+
+All of the test files and most of the rest of the files under tjs/ are
+symlinks to the canonical tests above the tjs/ directory.
+
+Also all of the html files (and other stuff) generated by the make
+commands goes under tjs/var/. These files never get checked into the
+Jemplate repository.
+
@@ -1,37 +0,0 @@
-proto = new Subclass('Test.Jemplate', 'Test.Base');
-
-proto.init = function() {
-    Test.Base.prototype.init.call(this);
-    this.block_class = 'Test.Jemplate.Block';
-}
-
-proto = Subclass('Test.Jemplate.Block', 'Test.Base.Block');
-
-proto.init = function() {
-    Test.Base.Block.prototype.init.call(this);
-    this.filter_object = new Test.Jemplate.Filter();
-}
-
-proto = new Subclass('Test.Jemplate.Filter', 'Test.Base.Filter');
-
-proto.jemplate_process = function(content, block) {
-    var template;
-    if (content.match(/\n/))
-        template = content.split(/\n/)[0];
-    else
-        template = content;
-
-    var j = new Jemplate();
-    var data = block.data.context;
-    var result = Jemplate.process(template, data);
-    return result;
-}
-
-proto.raw_context = function( content, block ) {
-    try {
-        var context = eval("("+content+")");
-        block.data.context = context;
-    } catch(e) {
-        alert(e);
-    }
-}
@@ -1,76 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(6);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Basic Substitution1
---- context
-{"name":"Wally"}
---- jemplate
-hello.html
-Hello, [% name %]
---- output
-Hello, Wally
-
-=== Basic Substitution2
---- context
-{"name":"Yann"}
---- jemplate: hello.html
---- output
-Hello, Yann
-
-=== Operator "=="
---- jemplate
-operator1.html
-[%- IF "abc" == "abc" -%]
-same
-[%- END %]
---- output
-same
-
-=== Operator "!="
---- jemplate
-operator2.html
-[%- IF "abc" != "def" -%]
-not same
-[%- END -%]
-
---- output
-not same
-
-=== Operator concat "+"
---- jemplate
-operator3.html
-[%- "abc" _ "def" -%]
-
---- output
-abcdef
-
-=== Array index fetch"
---- jemplate
-basic_array1.html
-[%- JAVASCRIPT -%]
-stash.set( "simple_list", ["a","b","c"] );
-stash.set( "mylist", [["a","b","c"],["d","e","f"],["h","i","j"]] );
-[%- END -%]
-a = [% simple_list.0 %]
-a = [% mylist.0.0 %]
-c = [% mylist.0.2 %]
-e = [% mylist.1.1 %]
-j = [% mylist.2.2 -%]
-
---- output
-a = a
-a = a
-c = c
-e = e
-j = j
-
-*/
@@ -0,0 +1,125 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Getopt::Long    qw[];
+use HTTP::Daemon    qw[];
+use HTTP::Response  qw[];
+use HTTP::Status    qw[RC_FORBIDDEN];
+use LWP::MediaTypes qw[guess_media_type];
+use Path::Class     qw[];
+use Pod::Usage      qw[];
+use JSON;
+
+$SIG{'PIPE'} = 'IGNORE';
+
+Getopt::Long::Configure( 'no_ignore_case' );
+Getopt::Long::Configure( 'bundling' );
+Getopt::Long::GetOptions(
+    'p|port=i' => \( my $port = 8080 ),
+    'H|host=s' => \( my $host ),
+    'h|help|?' => sub { Pod::Usage::pod2usage( { -verbose => 1, -exitval => 0 } ) },
+    'm|man'    => sub { Pod::Usage::pod2usage( { -verbose => 2, -exitval => 0 } ) },
+) or exit 1;
+
+#my $base = Path::Class::File->new($0)->parent->parent->absolute;
+my $base = Path::Class::Dir->new; # Use the CWD
+my %args = ( LocalPort => $port, ReuseAddr => 1 );
+
+if ( defined $host ) {
+    $args{LocalHost} = $host;
+}
+
+my $daemon = HTTP::Daemon->new(%args)
+  or die("Failed to create daemon: $!\n");
+
+print "Please contact me at: <URL:", $daemon->url, ">\n";
+
+while ( my $connection = $daemon->accept ) {
+
+    while ( my $request = $connection->get_request ) {
+
+        if ( $request->url->path =~ /^\/ajax\b/ ) {
+            my $response = HTTP::Response->new( 200, 'OK' );
+            $response->header( 'Connection'    => 'close' );
+            $response->header( 'Cache-Control' => 'no-cache, max-age=0' );
+            my $content = encode_json { message => 'abcdefghijklmnopqrstuvwxy' };
+            $response->content( $content );
+            $response->content_length( length $content );
+            $response->content_type( "text/plain" );
+            $response->last_modified( time );
+
+            $connection->send_response( $response );
+
+            next;
+        }
+
+        my $file = $request->url->path =~ m|^/share|
+          ? $base->parent->file( $request->url->path )
+          : $request->url->path eq '/'
+              ? $base->file('index.html')
+              : $base->file( $request->url->path );
+
+        $file = $file->cleanup->absolute;
+
+        if ( -e $file && $request->method eq 'GET' ) {
+
+            print "$file\n";
+
+            my $stat     = $file->stat;
+            my $type     = ( guess_media_type( $file->stringify ) )[0];
+            my $response = HTTP::Response->new( 200, 'OK' );
+            $response->header( 'Connection'    => 'close' );
+            $response->header( 'Cache-Control' => 'no-cache, max-age=0' );
+
+            if ( 0 && $request->if_modified_since || 0 == $stat->mtime ) {
+                $response->code(304);
+                $response->message('Not Modified');
+            }
+            else {
+                $response->content( scalar $file->slurp );
+                $response->content_length( $stat->size );
+                $response->content_type($type);
+                $response->last_modified( $stat->mtime );
+            }
+
+            $connection->send_response($response);
+        }
+        else {
+
+            print "$file FORBIDDEN\n";
+
+            $connection->send_error(RC_FORBIDDEN);
+            $connection->close;
+        }
+    }
+}
+
+__END__
+
+=head1 NAME
+
+daemon - Jemplate test daemon
+
+=head1 SYNOPSIS
+
+daemon [-p]
+
+ Options:
+   -H --host   Host to listen to
+   -p --port   Port to listen to
+   -m --man    Displays manpage
+   -h --help   Displays this help
+
+=head1 COMMAND LINE OPTIONS
+
+=head2 --host
+
+Host to listen to. Defaults to listen to the given port on all interfaces.
+
+=head2 --port
+
+Port to listen to. Defaults to listen to 8080.
+
+=cut
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IO::All;
+
+my $output_dir = 'var/jt';
+
+my %jemplates;
+
+for my $test (glob '../t/*.t.js') {
+    my $file_contents = io($test)->all;
+    my (@jemplates) = ($file_contents =~ /(^--- jemplate\n.*?)(?=^---|\z)/gsm);
+    for my $jemplate (@jemplates) {
+        my ($ignore, $jemplate_file, $jemplate_content) =
+            split /\n/, $jemplate, 3;
+        $jemplates{$jemplate_file} = $jemplate_content;
+    }
+}
+
+mkdir $output_dir unless -d $output_dir;
+for my $jemplate_file (sort keys %jemplates) {
+    io("$output_dir/$jemplate_file")->print($jemplates{$jemplate_file});
+}
+
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IO::All;
+
+my $output_dir = 'jemplate';
+
+my %jemplates;
+
+for my $test (glob 't/*.t.js') {
+    my $file_contents = io($test)->all;
+    my (@jemplates) = ($file_contents =~ /(^--- jemplate\n.*?)(?=^---|\z)/gsm);
+    for my $jemplate (@jemplates) {
+        my ($ignore, $jemplate_file, $jemplate_content) =
+            split /\n/, $jemplate, 3;
+        $jemplates{$jemplate_file} = $jemplate_content;
+    }
+}
+
+mkdir $output_dir unless -d $output_dir;
+for my $jemplate_file (sort keys %jemplates) {
+    io("$output_dir/$jemplate_file")->print($jemplates{$jemplate_file});
+}
+
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Template;
+use IO::All;
+use YAML;
+
+my ($template_path, $output_path) = @ARGV;
+my $config = YAML::LoadFile('config.yaml');
+
+my $test_file_name = $output_path;
+($test_file_name =~ s/^var\//t\//) or
+($test_file_name =~ s/^/t\//) or die;
+$test_file_name =~ s/\.html$/.js/ or die;
+
+my $t = Template->new(
+    {
+        INCLUDE_PATH => [$config->{templates}],
+    }
+);
+
+my $all_test_files = [ glob('*.t.html') ];
+$all_test_files = [ map { s/^var\///; $_ } glob('var/*.t.html') ]
+    unless @$all_test_files;
+
+my $data = {
+    %$config,
+    test_file => $test_file_name,
+    all_test_files => $all_test_files,
+};
+my $result;
+
+$t->process($template_path, $data, \$result) or die $t->error;
+
+io($output_path)->print($result);
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+JEMPLATE="perl -I../.. ../../jemplate"
+DAEMON_PID=
+
+function run() {
+    $JEMPLATE $* > runtime.js
+    echo -e "\n"
+    echo -e "*** Testing: $*"
+    echo -e "*** Press return when you're ready to continue ***";
+    read;
+}
+
+function quit() {
+    [ -n "$DAEMON_PID" ] && kill -9 $DAEMON_PID
+    exit
+}
+
+trap "quit" SIGINT SIGTERM
+make all
+cd var
+../bin/daemon -p 8081 &
+DAEMON_PID=$!
+
+run --runtime=standard
+run --runtime=yui
+run --runtime=jquery
+
+quit
@@ -1,56 +0,0 @@
-var t = new Test.Jemplate();
-t.plan(1);
-t.pass('XXX - Finish writing this test file');
-
-/* Test
-=== Foo
---- xxx
-yyy
-
-*/
-// NODE CLASS
-
-/*
-=item * val = getName()
-
-This returns the name assigned to this object by the serverside code.
-
-If no name has been set, then this will return "E<lt>no nameE<gt>".
-
-*/
-
-/*
-,getName: function () { return this.name ? this.name : '<no name>'; } */
-
-/*
-
------------------------------------------
-// JEMPLATE CALL
-
-while(!iterator.atEnd()) {
-  var node = iterator.get();
-  tableBody += Jemplate.process('nl_table_row.html', { node: node, mode: this.mode });
-}
-this.viewer.innerHTML += tableBody;
-
-------------------------------------------
-// TEMPLATE
-
-<tr>
-  <td>
-    <a href="" id="node-[% node.id %]-list-item">
-    [% node.getName() %] - [% node.name %]
-    </a>
-  </td>
-  [% IF mode == 'updated' %]
-  <td id="node-[% node.id %]-list-timestamp">
-    [% node.updated_on_formatted %]
-  </td>
-  [% END %]
-</tr>
-
--------------------------------------------
-
-// node.getName() outputs nothing, but node.name outputs correctly. the getName() method definitely works.
-
-*/
@@ -1,6 +1,10 @@
 index_title: Jemplate Testing
 import_libs:
-  - ../Test/Jemplate.js
-  - JemplateRuntime.js
-  - jemplate.js
-  - jemplate2.js
+  - js/Test/Jemplate.js
+  - js/JemplateRuntime.js
+  - js/jemplate.js
+  - js/jemplate2.js
+  - js/jemplate_dummy_plugin.js
+  - js/global.js
+
+templates: template
@@ -1,24 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== DEFAULT directive works
---- context
-{"foo":"one"}
---- jemplate
-default.html
-[% DEFAULT foo = 'two', bar = 'three' -%]
-[% DEFAULT bar = 'four' -%]
-[% foo %] | [% bar %]
---- output
-one | three
-
-*/
@@ -1,74 +0,0 @@
-
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(5);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test BLOCK/SET/FOR/PROCESS
---- jemplate
-directives1.html
-[% BLOCK foo -%]
-I <3 Sushi
-[% END -%]
-[% SET list = [3, 4, 5] -%]
-[% FOR i = list -%]
-[% PROCESS foo -%]
-[% END -%]
---- output
-I <3 Sushi
-I <3 Sushi
-I <3 Sushi
-
-=== Test WHILE/IF/ELSE
---- jemplate
-directives2.html
-[% SET num = 4 -%]
-[% array = [] -%]
-[% WHILE num < 7 -%]
-[% IF num % 2 %][% CALL array.push('Odd') %][% ELSE %][% CALL array.push('Even') %][% END -%]
-[% num = num + 1 -%]
-[% END -%]
-[% array.join('***') %]
---- output
-Even***Odd***Even
-
-=== Test JAVASCRIPT
---- jemplate
-directives3.html
-[%- JAVASCRIPT -%]
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-[%- END -%]
-Key1: [% obj.key1 %]
-Key2: [% obj.key2 %]
---- output
-Key1: val1
-Key2: val2
-
-=== Test FOR i IN obj
---- jemplate
-directives4.html
-[%- JAVASCRIPT -%]
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-[%- END -%]
-[%- FOR key IN obj -%]
-[% key %]: [% obj.$key %]
-[% END -%]
---- output
-key1: val1
-key2: val2
-=== Test NEXT
---- jemplate
-directives5.html
-[%- FOR i IN [1,2,3,4,5,6] -%]
-[%- NEXT IF i % 2 == 0 -%]
-I = [% i %];
-[%- END -%]
---- output
-I = 1;I = 3;I = 5;
-*/
@@ -1,145 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(7);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test indent
---- jemplate
-filters_indent.html
-[% FILTER indent -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent(3) -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent('2') -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent(0) -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% text = 'The cat sat on the mat';
-   text | indent('> ') | indent('+') %]
---- output
-    1
-    2
-    3
-    4
-#
-   1
-   2
-   3
-   4
-#
-  1
-  2
-  3
-  4
-#
-1
-2
-3
-4
-#
-+> The cat sat on the mat
-=== Test truncate
---- jemplate
-filters_truncate.html
-[% a = '1234567890' -%]
-[% a | truncate(5)  %]
-[% a | truncate(10) %]
-[% a | truncate(15) %]
-[% a | truncate(2)  %]
-[% a = '1234567890123456789012345678901234567890' -%]
-[% a | truncate  %]
---- output
-12...
-1234567...
-1234567890
-...
-12345678901234567890123456789...
-
-=== Test null
---- jemplate
-filters_null.html
-[%- "Ils ont les chapeaux ronds, vive la bretagne" | null -%]
---- output
-=== Test uri
---- jemplate
-filters_uri.html
-[% "my file.html" FILTER uri %]
-[% "my<file & your>file.html" FILTER uri %]
-[% "my<file & your>file.html" | uri | html %]
-[% "guitar&amp;file.html" | uri %]
-[% "guitar&amp;file.html" | uri | html %]
---- output
-my%20file.html
-my%3Cfile%20&%20your%3Efile.html
-my%3Cfile%20&amp;%20your%3Efile.html
-guitar&amp;file.html
-guitar&amp;amp;file.html
-
-=== Test html 
---- jemplate
-filters_html.html
-[% FILTER html %]This is some html text
-All the <tags> should be escaped & protected
-[% END %]
-[% text = "The <cat> sat on the <mat>" %]
-[% text FILTER html %]
-[% FILTER html %]
-"It isn't what I expected", he replied.[% END %]
---- output
-This is some html text
-All the &lt;tags&gt; should be escaped &amp; protected
-
-
-The &lt;cat&gt; sat on the &lt;mat&gt;
-
-&quot;It isn't what I expected&quot;, he replied.
-
-=== Test repeat
---- jemplate
-filters_repeat.html
-[% "foo..." FILTER repeat(5) %]
---- output
-foo...foo...foo...foo...foo...
-
-=== Test replace
---- jemplate
-filters_replace.html
-[%- text = "The cat sat on the mat" %]
-[%- text FILTER replace(' ', '_') %]
-[% text FILTER replace('sat', 'shat') +%]
-[% text | replace('at', 'plat') +%]
-[% text = 'The <=> operator, blah, blah' %]
-[%- text | html | replace('blah', 'rhubarb') %]
---- output
-The_cat_sat_on_the_mat
-The cat shat on the mat
-The cplat splat on the mplat
-The &lt;=&gt; operator, rhubarb, rhubarb
-
-*/
@@ -1,23 +0,0 @@
-var t = new Test.Jemplate();
-
-t.plan(1);
-
-t.compile();
-
-var j = new Jemplate();
-
-var test1 = t.state.blocks.shift();
-t.is(
-    j.process('greetings', {name: 'Jesus'}),
-    test1.data.output,
-    test1.name
-);
-
-/* Test
-=== Include files with path names.
---- output
-English: Howdy Jesus. How are you?
-
-Spanish: Hola Jesus. Como estas?
-
-*/
@@ -1,119 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(9);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test each method
---- jemplate
-hash_each.html
-[%# This is a bit convoluted because sort order can not be guaranteed -%]
-[%# please fix it if you can find a better way... -%]
-[%# There are a few other tests below that are very similar -%]
-[% SET hash = { a=1 b=2 c=3 };
-   SET list = hash.each;
-   FOR kindex IN [0,2,4];
-     SET vindex = kindex + 1;
-     SET key = list.${kindex};
-     SET value = list.${vindex};
-     (key AND hash.${key} == value) ? 1 : 0;
-   END %]
---- output
-111
-
-=== Test exists method
---- jemplate
-hash_exists.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.exists('b') ? 1 : 0 %]
-[% a.exists('z') ? 1 : 0 %]
---- output
-1
-0
-
-=== Test import method TODO
---- jemplate
-hash_import.html
-[%# SET a = { a=1 b=2 c=3 } -%]
-[%# CALL a.import({ a=2 b=1 d=4 e=5 }) -%]
-[%# a.exists('c') ? 1 : 0 %]1
-[%# a.exists('d') ? 1 : 0 %]1
-[%# a.a %]2
-[%# a.e %]5
---- output
-1
-1
-2
-5
-
-=== Test keys method
---- jemplate
-hash_keys.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.keys.sort.join(' ') %]
---- output
-a b c
-
-=== Test list method
---- jemplate
-hash_list.html
-[% SET hash = { a=1 b=2 c=3 } -%]
-[% hash.list('keys').sort.join(' ') %]
-[% hash.list('values').sort.join(' ') %]
-[% SET list = hash.list('each');
-   FOR kindex IN [0,2,4];
-     SET vindex = kindex + 1;
-     SET key = list.${kindex};
-     SET value = list.${vindex};
-     (key AND hash.${key} == value) ? 1 : 0;
-   END %]
-[% SET list = hash.list();
-   FOR entry IN list;
-     SET key = entry.key;
-     SET value = entry.value;
-     (hash.${key} == value) ? 1 : 0;
-   END %]
---- output
-a b c
-1 2 3
-111
-111
-
-=== Test nsort method
---- jemplate
-hash_nsort.html
-[% SET a = { '499'='c' '5'='a' '50'='b' } -%]
-[% a.nsort.join(' ') %]
---- output
-5 50 499
-
-=== Test size method
---- jemplate
-hash_size.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.size %]
---- output
-3
-
-=== Test sort method
---- jemplate
-hash_sort.html
-[% SET a = { ac=1 b=2 aa=3 } -%]
-[% a.sort.join(' ') %]
---- output
-aa ac b
-
-=== Test values method
---- jemplate
-hash_values.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.values.nsort.join(' ') %]
---- output
-1 2 3
-
-*/
@@ -1,43 +0,0 @@
-var t = new Test.Jemplate();
-
-t.plan(3);
-
-t.compile();
-
-var j = new Jemplate();
-
-var test1 = t.state.blocks.shift();
-t.is(
-    j.process('hello', {name: 'Wally'}),
-    test1.data.output,
-    test1.name
-);
-
-var test2 = t.state.blocks.shift();
-t.is(
-    j.process('hello'),
-    test2.data.output,
-    test2.name
-);
-
-var j = new Jemplate({DEBUG_UNDEF: true});
-try {
-    j.process('hello', {namexxx: 'Bogey'});
-}
-catch(e) {
-    t.is(
-        String(e),
-        "undefined value found while using DEGUG_UNDEF",
-        "undefined value throws error"
-    );
-}
-
-/* Test
-=== Test second jemplate file (jemplate2.js)
---- output
-Hello, Wally!
-=== Test undefined value
---- output
-Hello, !
-
-*/
@@ -0,0 +1 @@
+Howdy <! name !>. How are you?
@@ -0,0 +1 @@
+Hola <! name !>. Como estas?
@@ -0,0 +1,3 @@
+English: <! INCLUDE greeting/english !>
+
+Spanish: <! InCluDe greeting/spanish !>
@@ -0,0 +1,5 @@
+<! BLOCK importantMessage !>
+    Message is <b><! message !></b>
+<! END !>
+<!- CLEAR !>
+Hello, <! who !>!
@@ -1,19 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test join
---- jemplate
-join.html
-[% a = ['foo', 'bar', 'baz'] %]
-[%- a.join('::') -%]
---- output
-foo::bar::baz
-*/
@@ -1,3408 +0,0 @@
-(function(){
-/*
- * jQuery 1.2.3 - New Wave Javascript
- *
- * Copyright (c) 2008 John Resig (jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
- * $Rev: 4663 $
- */
-
-// Map over jQuery in case of overwrite
-if ( window.jQuery )
-	var _jQuery = window.jQuery;
-
-var jQuery = window.jQuery = function( selector, context ) {
-	// The jQuery object is actually just the init constructor 'enhanced'
-	return new jQuery.prototype.init( selector, context );
-};
-
-// Map over the $ in case of overwrite
-if ( window.$ )
-	var _$ = window.$;
-	
-// Map the jQuery namespace to the '$' one
-window.$ = jQuery;
-
-// A simple way to check for HTML strings or ID strings
-// (both of which we optimize for)
-var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
-
-// Is it a simple selector
-var isSimple = /^.[^:#\[\.]*$/;
-
-jQuery.fn = jQuery.prototype = {
-	init: function( selector, context ) {
-		// Make sure that a selection was provided
-		selector = selector || document;
-
-		// Handle $(DOMElement)
-		if ( selector.nodeType ) {
-			this[0] = selector;
-			this.length = 1;
-			return this;
-
-		// Handle HTML strings
-		} else if ( typeof selector == "string" ) {
-			// Are we dealing with HTML string or an ID?
-			var match = quickExpr.exec( selector );
-
-			// Verify a match, and that no context was specified for #id
-			if ( match && (match[1] || !context) ) {
-
-				// HANDLE: $(html) -> $(array)
-				if ( match[1] )
-					selector = jQuery.clean( [ match[1] ], context );
-
-				// HANDLE: $("#id")
-				else {
-					var elem = document.getElementById( match[3] );
-
-					// Make sure an element was located
-					if ( elem )
-						// Handle the case where IE and Opera return items
-						// by name instead of ID
-						if ( elem.id != match[3] )
-							return jQuery().find( selector );
-
-						// Otherwise, we inject the element directly into the jQuery object
-						else {
-							this[0] = elem;
-							this.length = 1;
-							return this;
-						}
-
-					else
-						selector = [];
-				}
-
-			// HANDLE: $(expr, [context])
-			// (which is just equivalent to: $(content).find(expr)
-			} else
-				return new jQuery( context ).find( selector );
-
-		// HANDLE: $(function)
-		// Shortcut for document ready
-		} else if ( jQuery.isFunction( selector ) )
-			return new jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
-
-		return this.setArray(
-			// HANDLE: $(array)
-			selector.constructor == Array && selector ||
-
-			// HANDLE: $(arraylike)
-			// Watch for when an array-like object, contains DOM nodes, is passed in as the selector
-			(selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) ||
-
-			// HANDLE: $(*)
-			[ selector ] );
-	},
-	
-	// The current version of jQuery being used
-	jquery: "1.2.3",
-
-	// The number of elements contained in the matched element set
-	size: function() {
-		return this.length;
-	},
-	
-	// The number of elements contained in the matched element set
-	length: 0,
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-		return num == undefined ?
-
-			// Return a 'clean' array
-			jQuery.makeArray( this ) :
-
-			// Return just the object
-			this[ num ];
-	},
-	
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-		// Build a new jQuery matched element set
-		var ret = jQuery( elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-	
-	// Force the current matched set of elements to become
-	// the specified array of elements (destroying the stack in the process)
-	// You should use pushStack() in order to do this, but maintain the stack
-	setArray: function( elems ) {
-		// Resetting the length to 0, then using the native Array push
-		// is a super-fast way to populate an object with array-like properties
-		this.length = 0;
-		Array.prototype.push.apply( this, elems );
-		
-		return this;
-	},
-
-	// Execute a callback for every element in the matched set.
-	// (You can seed the arguments with an array of args, but this is
-	// only used internally.)
-	each: function( callback, args ) {
-		return jQuery.each( this, callback, args );
-	},
-
-	// Determine the position of an element within 
-	// the matched set of elements
-	index: function( elem ) {
-		var ret = -1;
-
-		// Locate the position of the desired element
-		this.each(function(i){
-			if ( this == elem )
-				ret = i;
-		});
-
-		return ret;
-	},
-
-	attr: function( name, value, type ) {
-		var options = name;
-		
-		// Look for the case where we're accessing a style value
-		if ( name.constructor == String )
-			if ( value == undefined )
-				return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined;
-
-			else {
-				options = {};
-				options[ name ] = value;
-			}
-		
-		// Check to see if we're setting style values
-		return this.each(function(i){
-			// Set all the styles
-			for ( name in options )
-				jQuery.attr(
-					type ?
-						this.style :
-						this,
-					name, jQuery.prop( this, options[ name ], type, i, name )
-				);
-		});
-	},
-
-	css: function( key, value ) {
-		// ignore negative width and height values
-		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
-			value = undefined;
-		return this.attr( key, value, "curCSS" );
-	},
-
-	text: function( text ) {
-		if ( typeof text != "object" && text != null )
-			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
-
-		var ret = "";
-
-		jQuery.each( text || this, function(){
-			jQuery.each( this.childNodes, function(){
-				if ( this.nodeType != 8 )
-					ret += this.nodeType != 1 ?
-						this.nodeValue :
-						jQuery.fn.text( [ this ] );
-			});
-		});
-
-		return ret;
-	},
-
-	wrapAll: function( html ) {
-		if ( this[0] )
-			// The elements to wrap the target around
-			jQuery( html, this[0].ownerDocument )
-				.clone()
-				.insertBefore( this[0] )
-				.map(function(){
-					var elem = this;
-
-					while ( elem.firstChild )
-						elem = elem.firstChild;
-
-					return elem;
-				})
-				.append(this);
-
-		return this;
-	},
-
-	wrapInner: function( html ) {
-		return this.each(function(){
-			jQuery( this ).contents().wrapAll( html );
-		});
-	},
-
-	wrap: function( html ) {
-		return this.each(function(){
-			jQuery( this ).wrapAll( html );
-		});
-	},
-
-	append: function() {
-		return this.domManip(arguments, true, false, function(elem){
-			if (this.nodeType == 1)
-				this.appendChild( elem );
-		});
-	},
-
-	prepend: function() {
-		return this.domManip(arguments, true, true, function(elem){
-			if (this.nodeType == 1)
-				this.insertBefore( elem, this.firstChild );
-		});
-	},
-	
-	before: function() {
-		return this.domManip(arguments, false, false, function(elem){
-			this.parentNode.insertBefore( elem, this );
-		});
-	},
-
-	after: function() {
-		return this.domManip(arguments, false, true, function(elem){
-			this.parentNode.insertBefore( elem, this.nextSibling );
-		});
-	},
-
-	end: function() {
-		return this.prevObject || jQuery( [] );
-	},
-
-	find: function( selector ) {
-		var elems = jQuery.map(this, function(elem){
-			return jQuery.find( selector, elem );
-		});
-
-		return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
-			jQuery.unique( elems ) :
-			elems );
-	},
-
-	clone: function( events ) {
-		// Do the clone
-		var ret = this.map(function(){
-			if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
-				// IE copies events bound via attachEvent when
-				// using cloneNode. Calling detachEvent on the
-				// clone will also remove the events from the orignal
-				// In order to get around this, we use innerHTML.
-				// Unfortunately, this means some modifications to 
-				// attributes in IE that are actually only stored 
-				// as properties will not be copied (such as the
-				// the name attribute on an input).
-				var clone = this.cloneNode(true),
-					container = document.createElement("div");
-				container.appendChild(clone);
-				return jQuery.clean([container.innerHTML])[0];
-			} else
-				return this.cloneNode(true);
-		});
-
-		// Need to set the expando to null on the cloned set if it exists
-		// removeData doesn't work here, IE removes it from the original as well
-		// this is primarily for IE but the data expando shouldn't be copied over in any browser
-		var clone = ret.find("*").andSelf().each(function(){
-			if ( this[ expando ] != undefined )
-				this[ expando ] = null;
-		});
-		
-		// Copy the events from the original to the clone
-		if ( events === true )
-			this.find("*").andSelf().each(function(i){
-				if (this.nodeType == 3)
-					return;
-				var events = jQuery.data( this, "events" );
-
-				for ( var type in events )
-					for ( var handler in events[ type ] )
-						jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
-			});
-
-		// Return the cloned set
-		return ret;
-	},
-
-	filter: function( selector ) {
-		return this.pushStack(
-			jQuery.isFunction( selector ) &&
-			jQuery.grep(this, function(elem, i){
-				return selector.call( elem, i );
-			}) ||
-
-			jQuery.multiFilter( selector, this ) );
-	},
-
-	not: function( selector ) {
-		if ( selector.constructor == String )
-			// test special case where just one selector is passed in
-			if ( isSimple.test( selector ) )
-				return this.pushStack( jQuery.multiFilter( selector, this, true ) );
-			else
-				selector = jQuery.multiFilter( selector, this );
-
-		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
-		return this.filter(function() {
-			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
-		});
-	},
-
-	add: function( selector ) {
-		return !selector ? this : this.pushStack( jQuery.merge( 
-			this.get(),
-			selector.constructor == String ? 
-				jQuery( selector ).get() :
-				selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ?
-					selector : [selector] ) );
-	},
-
-	is: function( selector ) {
-		return selector ?
-			jQuery.multiFilter( selector, this ).length > 0 :
-			false;
-	},
-
-	hasClass: function( selector ) {
-		return this.is( "." + selector );
-	},
-	
-	val: function( value ) {
-		if ( value == undefined ) {
-
-			if ( this.length ) {
-				var elem = this[0];
-
-				// We need to handle select boxes special
-				if ( jQuery.nodeName( elem, "select" ) ) {
-					var index = elem.selectedIndex,
-						values = [],
-						options = elem.options,
-						one = elem.type == "select-one";
-					
-					// Nothing was selected
-					if ( index < 0 )
-						return null;
-
-					// Loop through all the selected options
-					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
-						var option = options[ i ];
-
-						if ( option.selected ) {
-							// Get the specifc value for the option
-							value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
-							
-							// We don't need an array for one selects
-							if ( one )
-								return value;
-							
-							// Multi-Selects return an array
-							values.push( value );
-						}
-					}
-					
-					return values;
-					
-				// Everything else, we just grab the value
-				} else
-					return (this[0].value || "").replace(/\r/g, "");
-
-			}
-
-			return undefined;
-		}
-
-		return this.each(function(){
-			if ( this.nodeType != 1 )
-				return;
-
-			if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
-				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
-					jQuery.inArray(this.name, value) >= 0);
-
-			else if ( jQuery.nodeName( this, "select" ) ) {
-				var values = value.constructor == Array ?
-					value :
-					[ value ];
-
-				jQuery( "option", this ).each(function(){
-					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
-						jQuery.inArray( this.text, values ) >= 0);
-				});
-
-				if ( !values.length )
-					this.selectedIndex = -1;
-
-			} else
-				this.value = value;
-		});
-	},
-	
-	html: function( value ) {
-		return value == undefined ?
-			(this.length ?
-				this[0].innerHTML :
-				null) :
-			this.empty().append( value );
-	},
-
-	replaceWith: function( value ) {
-		return this.after( value ).remove();
-	},
-
-	eq: function( i ) {
-		return this.slice( i, i + 1 );
-	},
-
-	slice: function() {
-		return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map(this, function(elem, i){
-			return callback.call( elem, i, elem );
-		}));
-	},
-
-	andSelf: function() {
-		return this.add( this.prevObject );
-	},
-
-	data: function( key, value ){
-		var parts = key.split(".");
-		parts[1] = parts[1] ? "." + parts[1] : "";
-
-		if ( value == null ) {
-			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-			
-			if ( data == undefined && this.length )
-				data = jQuery.data( this[0], key );
-
-			return data == null && parts[1] ?
-				this.data( parts[0] ) :
-				data;
-		} else
-			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
-				jQuery.data( this, key, value );
-			});
-	},
-
-	removeData: function( key ){
-		return this.each(function(){
-			jQuery.removeData( this, key );
-		});
-	},
-	
-	domManip: function( args, table, reverse, callback ) {
-		var clone = this.length > 1, elems; 
-
-		return this.each(function(){
-			if ( !elems ) {
-				elems = jQuery.clean( args, this.ownerDocument );
-
-				if ( reverse )
-					elems.reverse();
-			}
-
-			var obj = this;
-
-			if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
-				obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
-
-			var scripts = jQuery( [] );
-
-			jQuery.each(elems, function(){
-				var elem = clone ?
-					jQuery( this ).clone( true )[0] :
-					this;
-
-				// execute all scripts after the elements have been injected
-				if ( jQuery.nodeName( elem, "script" ) ) {
-					scripts = scripts.add( elem );
-				} else {
-					// Remove any inner scripts for later evaluation
-					if ( elem.nodeType == 1 )
-						scripts = scripts.add( jQuery( "script", elem ).remove() );
-
-					// Inject the elements into the document
-					callback.call( obj, elem );
-				}
-			});
-
-			scripts.each( evalScript );
-		});
-	}
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.prototype.init.prototype = jQuery.prototype;
-
-function evalScript( i, elem ) {
-	if ( elem.src )
-		jQuery.ajax({
-			url: elem.src,
-			async: false,
-			dataType: "script"
-		});
-
-	else
-		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
-
-	if ( elem.parentNode )
-		elem.parentNode.removeChild( elem );
-}
-
-jQuery.extend = jQuery.fn.extend = function() {
-	// copy reference to target object
-	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
-
-	// Handle a deep copy situation
-	if ( target.constructor == Boolean ) {
-		deep = target;
-		target = arguments[1] || {};
-		// skip the boolean and the target
-		i = 2;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target != "object" && typeof target != "function" )
-		target = {};
-
-	// extend jQuery itself if only one argument is passed
-	if ( length == 1 ) {
-		target = this;
-		i = 0;
-	}
-
-	for ( ; i < length; i++ )
-		// Only deal with non-null/undefined values
-		if ( (options = arguments[ i ]) != null )
-			// Extend the base object
-			for ( var name in options ) {
-				// Prevent never-ending loop
-				if ( target === options[ name ] )
-					continue;
-
-				// Recurse if we're merging object values
-				if ( deep && options[ name ] && typeof options[ name ] == "object" && target[ name ] && !options[ name ].nodeType )
-					target[ name ] = jQuery.extend( target[ name ], options[ name ] );
-
-				// Don't bring in undefined values
-				else if ( options[ name ] != undefined )
-					target[ name ] = options[ name ];
-
-			}
-
-	// Return the modified object
-	return target;
-};
-
-var expando = "jQuery" + (new Date()).getTime(), uuid = 0, windowData = {};
-
-// exclude the following css properties to add px
-var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;
-
-jQuery.extend({
-	noConflict: function( deep ) {
-		window.$ = _$;
-
-		if ( deep )
-			window.jQuery = _jQuery;
-
-		return jQuery;
-	},
-
-	// See test/unit/core.js for details concerning this function.
-	isFunction: function( fn ) {
-		return !!fn && typeof fn != "string" && !fn.nodeName && 
-			fn.constructor != Array && /function/i.test( fn + "" );
-	},
-	
-	// check if an element is in a (or is an) XML document
-	isXMLDoc: function( elem ) {
-		return elem.documentElement && !elem.body ||
-			elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
-	},
-
-	// Evalulates a script in a global context
-	globalEval: function( data ) {
-		data = jQuery.trim( data );
-
-		if ( data ) {
-			// Inspired by code by Andrea Giammarchi
-			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
-			var head = document.getElementsByTagName("head")[0] || document.documentElement,
-				script = document.createElement("script");
-
-			script.type = "text/javascript";
-			if ( jQuery.browser.msie )
-				script.text = data;
-			else
-				script.appendChild( document.createTextNode( data ) );
-
-			head.appendChild( script );
-			head.removeChild( script );
-		}
-	},
-
-	nodeName: function( elem, name ) {
-		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
-	},
-	
-	cache: {},
-	
-	data: function( elem, name, data ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// Compute a unique ID for the element
-		if ( !id ) 
-			id = elem[ expando ] = ++uuid;
-
-		// Only generate the data cache if we're
-		// trying to access or manipulate it
-		if ( name && !jQuery.cache[ id ] )
-			jQuery.cache[ id ] = {};
-		
-		// Prevent overriding the named cache with undefined values
-		if ( data != undefined )
-			jQuery.cache[ id ][ name ] = data;
-		
-		// Return the named cache data, or the ID for the element	
-		return name ?
-			jQuery.cache[ id ][ name ] :
-			id;
-	},
-	
-	removeData: function( elem, name ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// If we want to remove a specific section of the element's data
-		if ( name ) {
-			if ( jQuery.cache[ id ] ) {
-				// Remove the section of cache data
-				delete jQuery.cache[ id ][ name ];
-
-				// If we've removed all the data, remove the element's cache
-				name = "";
-
-				for ( name in jQuery.cache[ id ] )
-					break;
-
-				if ( !name )
-					jQuery.removeData( elem );
-			}
-
-		// Otherwise, we want to remove all of the element's data
-		} else {
-			// Clean up the element expando
-			try {
-				delete elem[ expando ];
-			} catch(e){
-				// IE has trouble directly removing the expando
-				// but it's ok with using removeAttribute
-				if ( elem.removeAttribute )
-					elem.removeAttribute( expando );
-			}
-
-			// Completely remove the data cache
-			delete jQuery.cache[ id ];
-		}
-	},
-
-	// args is for internal usage only
-	each: function( object, callback, args ) {
-		if ( args ) {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.apply( object[ name ], args ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length; i < length; i++ )
-					if ( callback.apply( object[ i ], args ) === false )
-						break;
-
-		// A special, fast, case for the most common use of each
-		} else {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.call( object[ name ], name, object[ name ] ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length, value = object[0]; 
-					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
-		}
-
-		return object;
-	},
-	
-	prop: function( elem, value, type, i, name ) {
-			// Handle executable functions
-			if ( jQuery.isFunction( value ) )
-				value = value.call( elem, i );
-				
-			// Handle passing in a number to a CSS property
-			return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
-				value + "px" :
-				value;
-	},
-
-	className: {
-		// internal only, use addClass("class")
-		add: function( elem, classNames ) {
-			jQuery.each((classNames || "").split(/\s+/), function(i, className){
-				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
-					elem.className += (elem.className ? " " : "") + className;
-			});
-		},
-
-		// internal only, use removeClass("class")
-		remove: function( elem, classNames ) {
-			if (elem.nodeType == 1)
-				elem.className = classNames != undefined ?
-					jQuery.grep(elem.className.split(/\s+/), function(className){
-						return !jQuery.className.has( classNames, className );	
-					}).join(" ") :
-					"";
-		},
-
-		// internal only, use is(".class")
-		has: function( elem, className ) {
-			return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
-		}
-	},
-
-	// A method for quickly swapping in/out CSS properties to get correct calculations
-	swap: function( elem, options, callback ) {
-		var old = {};
-		// Remember the old values, and insert the new ones
-		for ( var name in options ) {
-			old[ name ] = elem.style[ name ];
-			elem.style[ name ] = options[ name ];
-		}
-
-		callback.call( elem );
-
-		// Revert the old values
-		for ( var name in options )
-			elem.style[ name ] = old[ name ];
-	},
-
-	css: function( elem, name, force ) {
-		if ( name == "width" || name == "height" ) {
-			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
-		
-			function getWH() {
-				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
-				var padding = 0, border = 0;
-				jQuery.each( which, function() {
-					padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
-					border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
-				});
-				val -= Math.round(padding + border);
-			}
-		
-			if ( jQuery(elem).is(":visible") )
-				getWH();
-			else
-				jQuery.swap( elem, props, getWH );
-			
-			return Math.max(0, val);
-		}
-		
-		return jQuery.curCSS( elem, name, force );
-	},
-
-	curCSS: function( elem, name, force ) {
-		var ret;
-
-		// A helper method for determining if an element's values are broken
-		function color( elem ) {
-			if ( !jQuery.browser.safari )
-				return false;
-
-			var ret = document.defaultView.getComputedStyle( elem, null );
-			return !ret || ret.getPropertyValue("color") == "";
-		}
-
-		// We need to handle opacity special in IE
-		if ( name == "opacity" && jQuery.browser.msie ) {
-			ret = jQuery.attr( elem.style, "opacity" );
-
-			return ret == "" ?
-				"1" :
-				ret;
-		}
-		// Opera sometimes will give the wrong display answer, this fixes it, see #2037
-		if ( jQuery.browser.opera && name == "display" ) {
-			var save = elem.style.outline;
-			elem.style.outline = "0 solid black";
-			elem.style.outline = save;
-		}
-		
-		// Make sure we're using the right name for getting the float value
-		if ( name.match( /float/i ) )
-			name = styleFloat;
-
-		if ( !force && elem.style && elem.style[ name ] )
-			ret = elem.style[ name ];
-
-		else if ( document.defaultView && document.defaultView.getComputedStyle ) {
-
-			// Only "float" is needed here
-			if ( name.match( /float/i ) )
-				name = "float";
-
-			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
-
-			var getComputedStyle = document.defaultView.getComputedStyle( elem, null );
-
-			if ( getComputedStyle && !color( elem ) )
-				ret = getComputedStyle.getPropertyValue( name );
-
-			// If the element isn't reporting its values properly in Safari
-			// then some display: none elements are involved
-			else {
-				var swap = [], stack = [];
-
-				// Locate all of the parent display: none elements
-				for ( var a = elem; a && color(a); a = a.parentNode )
-					stack.unshift(a);
-
-				// Go through and make them visible, but in reverse
-				// (It would be better if we knew the exact display type that they had)
-				for ( var i = 0; i < stack.length; i++ )
-					if ( color( stack[ i ] ) ) {
-						swap[ i ] = stack[ i ].style.display;
-						stack[ i ].style.display = "block";
-					}
-
-				// Since we flip the display style, we have to handle that
-				// one special, otherwise get the value
-				ret = name == "display" && swap[ stack.length - 1 ] != null ?
-					"none" :
-					( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || "";
-
-				// Finally, revert the display styles back
-				for ( var i = 0; i < swap.length; i++ )
-					if ( swap[ i ] != null )
-						stack[ i ].style.display = swap[ i ];
-			}
-
-			// We should always get a number back from opacity
-			if ( name == "opacity" && ret == "" )
-				ret = "1";
-
-		} else if ( elem.currentStyle ) {
-			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
-
-			// From the awesome hack by Dean Edwards
-			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
-
-			// If we're not dealing with a regular pixel number
-			// but a number that has a weird ending, we need to convert it to pixels
-			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
-				// Remember the original values
-				var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left;
-
-				// Put in the new values to get a computed value out
-				elem.runtimeStyle.left = elem.currentStyle.left;
-				elem.style.left = ret || 0;
-				ret = elem.style.pixelLeft + "px";
-
-				// Revert the changed values
-				elem.style.left = style;
-				elem.runtimeStyle.left = runtimeStyle;
-			}
-		}
-
-		return ret;
-	},
-	
-	clean: function( elems, context ) {
-		var ret = [];
-		context = context || document;
-		// !context.createElement fails in IE with an error but returns typeof 'object'
-		if (typeof context.createElement == 'undefined') 
-			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
-
-		jQuery.each(elems, function(i, elem){
-			if ( !elem )
-				return;
-
-			if ( elem.constructor == Number )
-				elem = elem.toString();
-			
-			// Convert html string into DOM nodes
-			if ( typeof elem == "string" ) {
-				// Fix "XHTML"-style tags in all browsers
-				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
-					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
-						all :
-						front + "></" + tag + ">";
-				});
-
-				// Trim whitespace, otherwise indexOf won't work as expected
-				var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
-
-				var wrap =
-					// option or optgroup
-					!tags.indexOf("<opt") &&
-					[ 1, "<select multiple='multiple'>", "</select>" ] ||
-					
-					!tags.indexOf("<leg") &&
-					[ 1, "<fieldset>", "</fieldset>" ] ||
-					
-					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
-					[ 1, "<table>", "</table>" ] ||
-					
-					!tags.indexOf("<tr") &&
-					[ 2, "<table><tbody>", "</tbody></table>" ] ||
-					
-				 	// <thead> matched above
-					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
-					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
-					
-					!tags.indexOf("<col") &&
-					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
-
-					// IE can't serialize <link> and <script> tags normally
-					jQuery.browser.msie &&
-					[ 1, "div<div>", "</div>" ] ||
-					
-					[ 0, "", "" ];
-
-				// Go to html and back, then peel off extra wrappers
-				div.innerHTML = wrap[1] + elem + wrap[2];
-				
-				// Move to the right depth
-				while ( wrap[0]-- )
-					div = div.lastChild;
-				
-				// Remove IE's autoinserted <tbody> from table fragments
-				if ( jQuery.browser.msie ) {
-					
-					// String was a <table>, *may* have spurious <tbody>
-					var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
-						div.firstChild && div.firstChild.childNodes :
-						
-						// String was a bare <thead> or <tfoot>
-						wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
-							div.childNodes :
-							[];
-				
-					for ( var j = tbody.length - 1; j >= 0 ; --j )
-						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
-							tbody[ j ].parentNode.removeChild( tbody[ j ] );
-					
-					// IE completely kills leading whitespace when innerHTML is used	
-					if ( /^\s/.test( elem ) )	
-						div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
-				
-				}
-				
-				elem = jQuery.makeArray( div.childNodes );
-			}
-
-			if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
-				return;
-
-			if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
-				ret.push( elem );
-
-			else
-				ret = jQuery.merge( ret, elem );
-
-		});
-
-		return ret;
-	},
-	
-	attr: function( elem, name, value ) {
-		// don't set attributes on text and comment nodes
-		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
-			return undefined;
-
-		var fix = jQuery.isXMLDoc( elem ) ?
-			{} :
-			jQuery.props;
-
-		// Safari mis-reports the default selected property of a hidden option
-		// Accessing the parent's selectedIndex property fixes it
-		if ( name == "selected" && jQuery.browser.safari )
-			elem.parentNode.selectedIndex;
-		
-		// Certain attributes only work when accessed via the old DOM 0 way
-		if ( fix[ name ] ) {
-			if ( value != undefined )
-				elem[ fix[ name ] ] = value;
-
-			return elem[ fix[ name ] ];
-
-		} else if ( jQuery.browser.msie && name == "style" )
-			return jQuery.attr( elem.style, "cssText", value );
-
-		else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName( elem, "form" ) && (name == "action" || name == "method") )
-			return elem.getAttributeNode( name ).nodeValue;
-
-		// IE elem.getAttribute passes even for style
-		else if ( elem.tagName ) {
-
-			if ( value != undefined ) {
-				// We can't allow the type property to be changed (since it causes problems in IE)
-				if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
-					throw "type property can't be changed";
-
-				// convert the value to a string (all browsers do this but IE) see #1070
-				elem.setAttribute( name, "" + value );
-			}
-
-			if ( jQuery.browser.msie && /href|src/.test( name ) && !jQuery.isXMLDoc( elem ) ) 
-				return elem.getAttribute( name, 2 );
-
-			return elem.getAttribute( name );
-
-		// elem is actually elem.style ... set the style
-		} else {
-			// IE actually uses filters for opacity
-			if ( name == "opacity" && jQuery.browser.msie ) {
-				if ( value != undefined ) {
-					// IE has trouble with opacity if it does not have layout
-					// Force it by setting the zoom level
-					elem.zoom = 1; 
-	
-					// Set the alpha filter to set the opacity
-					elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
-						(parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
-				}
-	
-				return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
-					(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() :
-					"";
-			}
-
-			name = name.replace(/-([a-z])/ig, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			if ( value != undefined )
-				elem[ name ] = value;
-
-			return elem[ name ];
-		}
-	},
-	
-	trim: function( text ) {
-		return (text || "").replace( /^\s+|\s+$/g, "" );
-	},
-
-	makeArray: function( array ) {
-		var ret = [];
-
-		// Need to use typeof to fight Safari childNodes crashes
-		if ( typeof array != "array" )
-			for ( var i = 0, length = array.length; i < length; i++ )
-				ret.push( array[ i ] );
-		else
-			ret = array.slice( 0 );
-
-		return ret;
-	},
-
-	inArray: function( elem, array ) {
-		for ( var i = 0, length = array.length; i < length; i++ )
-			if ( array[ i ] == elem )
-				return i;
-
-		return -1;
-	},
-
-	merge: function( first, second ) {
-		// We have to loop this way because IE & Opera overwrite the length
-		// expando of getElementsByTagName
-
-		// Also, we need to make sure that the correct elements are being returned
-		// (IE returns comment nodes in a '*' query)
-		if ( jQuery.browser.msie ) {
-			for ( var i = 0; second[ i ]; i++ )
-				if ( second[ i ].nodeType != 8 )
-					first.push( second[ i ] );
-
-		} else
-			for ( var i = 0; second[ i ]; i++ )
-				first.push( second[ i ] );
-
-		return first;
-	},
-
-	unique: function( array ) {
-		var ret = [], done = {};
-
-		try {
-
-			for ( var i = 0, length = array.length; i < length; i++ ) {
-				var id = jQuery.data( array[ i ] );
-
-				if ( !done[ id ] ) {
-					done[ id ] = true;
-					ret.push( array[ i ] );
-				}
-			}
-
-		} catch( e ) {
-			ret = array;
-		}
-
-		return ret;
-	},
-
-	grep: function( elems, callback, inv ) {
-		var ret = [];
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( var i = 0, length = elems.length; i < length; i++ )
-			if ( !inv && callback( elems[ i ], i ) || inv && !callback( elems[ i ], i ) )
-				ret.push( elems[ i ] );
-
-		return ret;
-	},
-
-	map: function( elems, callback ) {
-		var ret = [];
-
-		// Go through the array, translating each of the items to their
-		// new value (or values).
-		for ( var i = 0, length = elems.length; i < length; i++ ) {
-			var value = callback( elems[ i ], i );
-
-			if ( value !== null && value != undefined ) {
-				if ( value.constructor != Array )
-					value = [ value ];
-
-				ret = ret.concat( value );
-			}
-		}
-
-		return ret;
-	}
-});
-
-var userAgent = navigator.userAgent.toLowerCase();
-
-// Figure out what browser is being used
-jQuery.browser = {
-	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
-	safari: /webkit/.test( userAgent ),
-	opera: /opera/.test( userAgent ),
-	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
-	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
-};
-
-var styleFloat = jQuery.browser.msie ?
-	"styleFloat" :
-	"cssFloat";
-	
-jQuery.extend({
-	// Check to see if the W3C box model is being used
-	boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
-	
-	props: {
-		"for": "htmlFor",
-		"class": "className",
-		"float": styleFloat,
-		cssFloat: styleFloat,
-		styleFloat: styleFloat,
-		innerHTML: "innerHTML",
-		className: "className",
-		value: "value",
-		disabled: "disabled",
-		checked: "checked",
-		readonly: "readOnly",
-		selected: "selected",
-		maxlength: "maxLength",
-		selectedIndex: "selectedIndex",
-		defaultValue: "defaultValue",
-		tagName: "tagName",
-		nodeName: "nodeName"
-	}
-});
-
-jQuery.each({
-	parent: function(elem){return elem.parentNode;},
-	parents: function(elem){return jQuery.dir(elem,"parentNode");},
-	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
-	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
-	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
-	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
-	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
-	children: function(elem){return jQuery.sibling(elem.firstChild);},
-	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
-}, function(name, fn){
-	jQuery.fn[ name ] = function( selector ) {
-		var ret = jQuery.map( this, fn );
-
-		if ( selector && typeof selector == "string" )
-			ret = jQuery.multiFilter( selector, ret );
-
-		return this.pushStack( jQuery.unique( ret ) );
-	};
-});
-
-jQuery.each({
-	appendTo: "append",
-	prependTo: "prepend",
-	insertBefore: "before",
-	insertAfter: "after",
-	replaceAll: "replaceWith"
-}, function(name, original){
-	jQuery.fn[ name ] = function() {
-		var args = arguments;
-
-		return this.each(function(){
-			for ( var i = 0, length = args.length; i < length; i++ )
-				jQuery( args[ i ] )[ original ]( this );
-		});
-	};
-});
-
-jQuery.each({
-	removeAttr: function( name ) {
-		jQuery.attr( this, name, "" );
-		if (this.nodeType == 1) 
-			this.removeAttribute( name );
-	},
-
-	addClass: function( classNames ) {
-		jQuery.className.add( this, classNames );
-	},
-
-	removeClass: function( classNames ) {
-		jQuery.className.remove( this, classNames );
-	},
-
-	toggleClass: function( classNames ) {
-		jQuery.className[ jQuery.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
-	},
-
-	remove: function( selector ) {
-		if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
-			// Prevent memory leaks
-			jQuery( "*", this ).add(this).each(function(){
-				jQuery.event.remove(this);
-				jQuery.removeData(this);
-			});
-			if (this.parentNode)
-				this.parentNode.removeChild( this );
-		}
-	},
-
-	empty: function() {
-		// Remove element nodes and prevent memory leaks
-		jQuery( ">*", this ).remove();
-		
-		// Remove any remaining nodes
-		while ( this.firstChild )
-			this.removeChild( this.firstChild );
-	}
-}, function(name, fn){
-	jQuery.fn[ name ] = function(){
-		return this.each( fn, arguments );
-	};
-});
-
-jQuery.each([ "Height", "Width" ], function(i, name){
-	var type = name.toLowerCase();
-	
-	jQuery.fn[ type ] = function( size ) {
-		// Get window width or height
-		return this[0] == window ?
-			// Opera reports document.body.client[Width/Height] properly in both quirks and standards
-			jQuery.browser.opera && document.body[ "client" + name ] || 
-			
-			// Safari reports inner[Width/Height] just fine (Mozilla and Opera include scroll bar widths)
-			jQuery.browser.safari && window[ "inner" + name ] ||
-			
-			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
-			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || document.body[ "client" + name ] :
-		
-			// Get document width or height
-			this[0] == document ?
-				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
-				Math.max( 
-					Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), 
-					Math.max(document.body["offset" + name], document.documentElement["offset" + name]) 
-				) :
-
-				// Get or set width or height on the element
-				size == undefined ?
-					// Get width or height on the element
-					(this.length ? jQuery.css( this[0], type ) : null) :
-
-					// Set the width or height on the element (default to pixels if value is unitless)
-					this.css( type, size.constructor == String ? size : size + "px" );
-	};
-});
-
-var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417 ?
-		"(?:[\\w*_-]|\\\\.)" :
-		"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
-	quickChild = new RegExp("^>\\s*(" + chars + "+)"),
-	quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),
-	quickClass = new RegExp("^([#.]?)(" + chars + "*)");
-
-jQuery.extend({
-	expr: {
-		"": function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},
-		"#": function(a,i,m){return a.getAttribute("id")==m[2];},
-		":": {
-			// Position Checks
-			lt: function(a,i,m){return i<m[3]-0;},
-			gt: function(a,i,m){return i>m[3]-0;},
-			nth: function(a,i,m){return m[3]-0==i;},
-			eq: function(a,i,m){return m[3]-0==i;},
-			first: function(a,i){return i==0;},
-			last: function(a,i,m,r){return i==r.length-1;},
-			even: function(a,i){return i%2==0;},
-			odd: function(a,i){return i%2;},
-
-			// Child Checks
-			"first-child": function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},
-			"last-child": function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},
-			"only-child": function(a){return !jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},
-
-			// Parent Checks
-			parent: function(a){return a.firstChild;},
-			empty: function(a){return !a.firstChild;},
-
-			// Text Check
-			contains: function(a,i,m){return (a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},
-
-			// Visibility
-			visible: function(a){return "hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},
-			hidden: function(a){return "hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},
-
-			// Form attributes
-			enabled: function(a){return !a.disabled;},
-			disabled: function(a){return a.disabled;},
-			checked: function(a){return a.checked;},
-			selected: function(a){return a.selected||jQuery.attr(a,"selected");},
-
-			// Form elements
-			text: function(a){return "text"==a.type;},
-			radio: function(a){return "radio"==a.type;},
-			checkbox: function(a){return "checkbox"==a.type;},
-			file: function(a){return "file"==a.type;},
-			password: function(a){return "password"==a.type;},
-			submit: function(a){return "submit"==a.type;},
-			image: function(a){return "image"==a.type;},
-			reset: function(a){return "reset"==a.type;},
-			button: function(a){return "button"==a.type||jQuery.nodeName(a,"button");},
-			input: function(a){return /input|select|textarea|button/i.test(a.nodeName);},
-
-			// :has()
-			has: function(a,i,m){return jQuery.find(m[3],a).length;},
-
-			// :header
-			header: function(a){return /h\d/i.test(a.nodeName);},
-
-			// :animated
-			animated: function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}
-		}
-	},
-	
-	// The regular expressions that power the parsing engine
-	parse: [
-		// Match: [@value='test'], [@foo]
-		/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
-
-		// Match: :contains('foo')
-		/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
-
-		// Match: :even, :last-chlid, #id, .class
-		new RegExp("^([:.#]*)(" + chars + "+)")
-	],
-
-	multiFilter: function( expr, elems, not ) {
-		var old, cur = [];
-
-		while ( expr && expr != old ) {
-			old = expr;
-			var f = jQuery.filter( expr, elems, not );
-			expr = f.t.replace(/^\s*,\s*/, "" );
-			cur = not ? elems = f.r : jQuery.merge( cur, f.r );
-		}
-
-		return cur;
-	},
-
-	find: function( t, context ) {
-		// Quickly handle non-string expressions
-		if ( typeof t != "string" )
-			return [ t ];
-
-		// check to make sure context is a DOM element or a document
-		if ( context && context.nodeType != 1 && context.nodeType != 9)
-			return [ ];
-
-		// Set the correct context (if none is provided)
-		context = context || document;
-
-		// Initialize the search
-		var ret = [context], done = [], last, nodeName;
-
-		// Continue while a selector expression exists, and while
-		// we're no longer looping upon ourselves
-		while ( t && last != t ) {
-			var r = [];
-			last = t;
-
-			t = jQuery.trim(t);
-
-			var foundToken = false;
-
-			// An attempt at speeding up child selectors that
-			// point to a specific element tag
-			var re = quickChild;
-			var m = re.exec(t);
-
-			if ( m ) {
-				nodeName = m[1].toUpperCase();
-
-				// Perform our own iteration and filter
-				for ( var i = 0; ret[i]; i++ )
-					for ( var c = ret[i].firstChild; c; c = c.nextSibling )
-						if ( c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName) )
-							r.push( c );
-
-				ret = r;
-				t = t.replace( re, "" );
-				if ( t.indexOf(" ") == 0 ) continue;
-				foundToken = true;
-			} else {
-				re = /^([>+~])\s*(\w*)/i;
-
-				if ( (m = re.exec(t)) != null ) {
-					r = [];
-
-					var merge = {};
-					nodeName = m[2].toUpperCase();
-					m = m[1];
-
-					for ( var j = 0, rl = ret.length; j < rl; j++ ) {
-						var n = m == "~" || m == "+" ? ret[j].nextSibling : ret[j].firstChild;
-						for ( ; n; n = n.nextSibling )
-							if ( n.nodeType == 1 ) {
-								var id = jQuery.data(n);
-
-								if ( m == "~" && merge[id] ) break;
-								
-								if (!nodeName || n.nodeName.toUpperCase() == nodeName ) {
-									if ( m == "~" ) merge[id] = true;
-									r.push( n );
-								}
-								
-								if ( m == "+" ) break;
-							}
-					}
-
-					ret = r;
-
-					// And remove the token
-					t = jQuery.trim( t.replace( re, "" ) );
-					foundToken = true;
-				}
-			}
-
-			// See if there's still an expression, and that we haven't already
-			// matched a token
-			if ( t && !foundToken ) {
-				// Handle multiple expressions
-				if ( !t.indexOf(",") ) {
-					// Clean the result set
-					if ( context == ret[0] ) ret.shift();
-
-					// Merge the result sets
-					done = jQuery.merge( done, ret );
-
-					// Reset the context
-					r = ret = [context];
-
-					// Touch up the selector string
-					t = " " + t.substr(1,t.length);
-
-				} else {
-					// Optimize for the case nodeName#idName
-					var re2 = quickID;
-					var m = re2.exec(t);
-					
-					// Re-organize the results, so that they're consistent
-					if ( m ) {
-						m = [ 0, m[2], m[3], m[1] ];
-
-					} else {
-						// Otherwise, do a traditional filter check for
-						// ID, class, and element selectors
-						re2 = quickClass;
-						m = re2.exec(t);
-					}
-
-					m[2] = m[2].replace(/\\/g, "");
-
-					var elem = ret[ret.length-1];
-
-					// Try to do a global search by ID, where we can
-					if ( m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem) ) {
-						// Optimization for HTML document case
-						var oid = elem.getElementById(m[2]);
-						
-						// Do a quick check for the existence of the actual ID attribute
-						// to avoid selecting by the name attribute in IE
-						// also check to insure id is a string to avoid selecting an element with the name of 'id' inside a form
-						if ( (jQuery.browser.msie||jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2] )
-							oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
-
-						// Do a quick check for node name (where applicable) so
-						// that div#foo searches will be really fast
-						ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
-					} else {
-						// We need to find all descendant elements
-						for ( var i = 0; ret[i]; i++ ) {
-							// Grab the tag name being searched for
-							var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*" : m[2];
-
-							// Handle IE7 being really dumb about <object>s
-							if ( tag == "*" && ret[i].nodeName.toLowerCase() == "object" )
-								tag = "param";
-
-							r = jQuery.merge( r, ret[i].getElementsByTagName( tag ));
-						}
-
-						// It's faster to filter by class and be done with it
-						if ( m[1] == "." )
-							r = jQuery.classFilter( r, m[2] );
-
-						// Same with ID filtering
-						if ( m[1] == "#" ) {
-							var tmp = [];
-
-							// Try to find the element with the ID
-							for ( var i = 0; r[i]; i++ )
-								if ( r[i].getAttribute("id") == m[2] ) {
-									tmp = [ r[i] ];
-									break;
-								}
-
-							r = tmp;
-						}
-
-						ret = r;
-					}
-
-					t = t.replace( re2, "" );
-				}
-
-			}
-
-			// If a selector string still exists
-			if ( t ) {
-				// Attempt to filter it
-				var val = jQuery.filter(t,r);
-				ret = r = val.r;
-				t = jQuery.trim(val.t);
-			}
-		}
-
-		// An error occurred with the selector;
-		// just return an empty set instead
-		if ( t )
-			ret = [];
-
-		// Remove the root context
-		if ( ret && context == ret[0] )
-			ret.shift();
-
-		// And combine the results
-		done = jQuery.merge( done, ret );
-
-		return done;
-	},
-
-	classFilter: function(r,m,not){
-		m = " " + m + " ";
-		var tmp = [];
-		for ( var i = 0; r[i]; i++ ) {
-			var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
-			if ( !not && pass || not && !pass )
-				tmp.push( r[i] );
-		}
-		return tmp;
-	},
-
-	filter: function(t,r,not) {
-		var last;
-
-		// Look for common filter expressions
-		while ( t && t != last ) {
-			last = t;
-
-			var p = jQuery.parse, m;
-
-			for ( var i = 0; p[i]; i++ ) {
-				m = p[i].exec( t );
-
-				if ( m ) {
-					// Remove what we just matched
-					t = t.substring( m[0].length );
-
-					m[2] = m[2].replace(/\\/g, "");
-					break;
-				}
-			}
-
-			if ( !m )
-				break;
-
-			// :not() is a special case that can be optimized by
-			// keeping it out of the expression list
-			if ( m[1] == ":" && m[2] == "not" )
-				// optimize if only one selector found (most common case)
-				r = isSimple.test( m[3] ) ?
-					jQuery.filter(m[3], r, true).r :
-					jQuery( r ).not( m[3] );
-
-			// We can get a big speed boost by filtering by class here
-			else if ( m[1] == "." )
-				r = jQuery.classFilter(r, m[2], not);
-
-			else if ( m[1] == "[" ) {
-				var tmp = [], type = m[3];
-				
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var a = r[i], z = a[ jQuery.props[m[2]] || m[2] ];
-					
-					if ( z == null || /href|src|selected/.test(m[2]) )
-						z = jQuery.attr(a,m[2]) || '';
-
-					if ( (type == "" && !!z ||
-						 type == "=" && z == m[5] ||
-						 type == "!=" && z != m[5] ||
-						 type == "^=" && z && !z.indexOf(m[5]) ||
-						 type == "$=" && z.substr(z.length - m[5].length) == m[5] ||
-						 (type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0) ^ not )
-							tmp.push( a );
-				}
-				
-				r = tmp;
-
-			// We can get a speed boost by handling nth-child here
-			} else if ( m[1] == ":" && m[2] == "nth-child" ) {
-				var merge = {}, tmp = [],
-					// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
-					test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
-						m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||
-						!/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
-					// calculate the numbers (first)n+(last) including if they are negative
-					first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
- 
-				// loop through all the elements left in the jQuery object
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
-
-					if ( !merge[id] ) {
-						var c = 1;
-
-						for ( var n = parentNode.firstChild; n; n = n.nextSibling )
-							if ( n.nodeType == 1 )
-								n.nodeIndex = c++;
-
-						merge[id] = true;
-					}
-
-					var add = false;
-
-					if ( first == 0 ) {
-						if ( node.nodeIndex == last )
-							add = true;
-					} else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
-						add = true;
-
-					if ( add ^ not )
-						tmp.push( node );
-				}
-
-				r = tmp;
-
-			// Otherwise, find the expression to execute
-			} else {
-				var fn = jQuery.expr[ m[1] ];
-				if ( typeof fn == "object" )
-					fn = fn[ m[2] ];
-
-				if ( typeof fn == "string" )
-					fn = eval("false||function(a,i){return " + fn + ";}");
-
-				// Execute it against the current filter
-				r = jQuery.grep( r, function(elem, i){
-					return fn(elem, i, m, r);
-				}, not );
-			}
-		}
-
-		// Return an array of filtered elements (r)
-		// and the modified expression string (t)
-		return { r: r, t: t };
-	},
-
-	dir: function( elem, dir ){
-		var matched = [];
-		var cur = elem[dir];
-		while ( cur && cur != document ) {
-			if ( cur.nodeType == 1 )
-				matched.push( cur );
-			cur = cur[dir];
-		}
-		return matched;
-	},
-	
-	nth: function(cur,result,dir,elem){
-		result = result || 1;
-		var num = 0;
-
-		for ( ; cur; cur = cur[dir] )
-			if ( cur.nodeType == 1 && ++num == result )
-				break;
-
-		return cur;
-	},
-	
-	sibling: function( n, elem ) {
-		var r = [];
-
-		for ( ; n; n = n.nextSibling ) {
-			if ( n.nodeType == 1 && (!elem || n != elem) )
-				r.push( n );
-		}
-
-		return r;
-	}
-});
-
-/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code orignated from 
- * Dean Edwards' addEvent library.
- */
-jQuery.event = {
-
-	// Bind an event to an element
-	// Original by Dean Edwards
-	add: function(elem, types, handler, data) {
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		// For whatever reason, IE has trouble passing the window object
-		// around, causing it to be cloned in the process
-		if ( jQuery.browser.msie && elem.setInterval != undefined )
-			elem = window;
-
-		// Make sure that the function being executed has a unique ID
-		if ( !handler.guid )
-			handler.guid = this.guid++;
-			
-		// if data is passed, bind to handler 
-		if( data != undefined ) { 
-			// Create temporary function pointer to original handler 
-			var fn = handler; 
-
-			// Create unique handler function, wrapped around original handler 
-			handler = function() { 
-				// Pass arguments and context to original handler 
-				return fn.apply(this, arguments); 
-			};
-
-			// Store data in unique handler 
-			handler.data = data;
-
-			// Set the guid of unique handler to the same of original handler, so it can be removed 
-			handler.guid = fn.guid;
-		}
-
-		// Init the element's event structure
-		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
-			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
-				// returned undefined or false
-				var val;
-
-				// Handle the second event of a trigger and when
-				// an event is called after a page has unloaded
-				if ( typeof jQuery == "undefined" || jQuery.event.triggered )
-					return val;
-		
-				val = jQuery.event.handle.apply(arguments.callee.elem, arguments);
-		
-				return val;
-			});
-		// Add elem as a property of the handle function
-		// This is to prevent a memory leak with non-native
-		// event in IE.
-		handle.elem = elem;
-			
-			// Handle multiple events seperated by a space
-			// jQuery(...).bind("mouseover mouseout", fn);
-			jQuery.each(types.split(/\s+/), function(index, type) {
-				// Namespaced event handlers
-				var parts = type.split(".");
-				type = parts[0];
-				handler.type = parts[1];
-
-				// Get the current list of functions bound to this event
-				var handlers = events[type];
-
-				// Init the event handler queue
-				if (!handlers) {
-					handlers = events[type] = {};
-		
-					// Check for a special event handler
-					// Only use addEventListener/attachEvent if the special
-					// events handler returns false
-					if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) {
-						// Bind the global event handler to the element
-						if (elem.addEventListener)
-							elem.addEventListener(type, handle, false);
-						else if (elem.attachEvent)
-							elem.attachEvent("on" + type, handle);
-					}
-				}
-
-				// Add the function to the element's handler list
-				handlers[handler.guid] = handler;
-
-				// Keep track of which events have been used, for global triggering
-				jQuery.event.global[type] = true;
-			});
-		
-		// Nullify elem to prevent memory leaks in IE
-		elem = null;
-	},
-
-	guid: 1,
-	global: {},
-
-	// Detach an event or set of events from an element
-	remove: function(elem, types, handler) {
-		// don't do events on text and comment nodes
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		var events = jQuery.data(elem, "events"), ret, index;
-
-		if ( events ) {
-			// Unbind all events for the element
-			if ( types == undefined || (typeof types == "string" && types.charAt(0) == ".") )
-				for ( var type in events )
-					this.remove( elem, type + (types || "") );
-			else {
-				// types is actually an event object here
-				if ( types.type ) {
-					handler = types.handler;
-					types = types.type;
-				}
-				
-				// Handle multiple events seperated by a space
-				// jQuery(...).unbind("mouseover mouseout", fn);
-				jQuery.each(types.split(/\s+/), function(index, type){
-					// Namespaced event handlers
-					var parts = type.split(".");
-					type = parts[0];
-					
-					if ( events[type] ) {
-						// remove the given handler for the given type
-						if ( handler )
-							delete events[type][handler.guid];
-			
-						// remove all handlers for the given type
-						else
-							for ( handler in events[type] )
-								// Handle the removal of namespaced events
-								if ( !parts[1] || events[type][handler].type == parts[1] )
-									delete events[type][handler];
-
-						// remove generic event handler if no more handlers exist
-						for ( ret in events[type] ) break;
-						if ( !ret ) {
-							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) {
-								if (elem.removeEventListener)
-									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
-								else if (elem.detachEvent)
-									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
-							}
-							ret = null;
-							delete events[type];
-						}
-					}
-				});
-			}
-
-			// Remove the expando if it's no longer used
-			for ( ret in events ) break;
-			if ( !ret ) {
-				var handle = jQuery.data( elem, "handle" );
-				if ( handle ) handle.elem = null;
-				jQuery.removeData( elem, "events" );
-				jQuery.removeData( elem, "handle" );
-			}
-		}
-	},
-
-	trigger: function(type, data, elem, donative, extra) {
-		// Clone the incoming data, if any
-		data = jQuery.makeArray(data || []);
-
-		if ( type.indexOf("!") >= 0 ) {
-			type = type.slice(0, -1);
-			var exclusive = true;
-		}
-
-		// Handle a global trigger
-		if ( !elem ) {
-			// Only trigger if we've ever bound an event for it
-			if ( this.global[type] )
-				jQuery("*").add([window, document]).trigger(type, data);
-
-		// Handle triggering a single element
-		} else {
-			// don't do events on text and comment nodes
-			if ( elem.nodeType == 3 || elem.nodeType == 8 )
-				return undefined;
-
-			var val, ret, fn = jQuery.isFunction( elem[ type ] || null ),
-				// Check to see if we need to provide a fake event, or not
-				event = !data[0] || !data[0].preventDefault;
-			
-			// Pass along a fake event
-			if ( event )
-				data.unshift( this.fix({ type: type, target: elem }) );
-
-			// Enforce the right trigger type
-			data[0].type = type;
-			if ( exclusive )
-				data[0].exclusive = true;
-
-			// Trigger the event
-			if ( jQuery.isFunction( jQuery.data(elem, "handle") ) )
-				val = jQuery.data(elem, "handle").apply( elem, data );
-
-			// Handle triggering native .onfoo handlers
-			if ( !fn && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
-				val = false;
-
-			// Extra functions don't get the custom event object
-			if ( event )
-				data.shift();
-
-			// Handle triggering of extra function
-			if ( extra && jQuery.isFunction( extra ) ) {
-				// call the extra function and tack the current return value on the end for possible inspection
-				ret = extra.apply( elem, val == null ? data : data.concat( val ) );
-				// if anything is returned, give it precedence and have it overwrite the previous value
-				if (ret !== undefined)
-					val = ret;
-			}
-
-			// Trigger the native events (except for clicks on links)
-			if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
-				this.triggered = true;
-				try {
-					elem[ type ]();
-				// prevent IE from throwing an error for some hidden elements
-				} catch (e) {}
-			}
-
-			this.triggered = false;
-		}
-
-		return val;
-	},
-
-	handle: function(event) {
-		// returned undefined or false
-		var val;
-
-		// Empty object is for triggered events with no data
-		event = jQuery.event.fix( event || window.event || {} ); 
-
-		// Namespaced event handlers
-		var parts = event.type.split(".");
-		event.type = parts[0];
-
-		var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
-		args.unshift( event );
-
-		for ( var j in handlers ) {
-			var handler = handlers[j];
-			// Pass in a reference to the handler function itself
-			// So that we can later remove it
-			args[0].handler = handler;
-			args[0].data = handler.data;
-
-			// Filter the functions by class
-			if ( !parts[1] && !event.exclusive || handler.type == parts[1] ) {
-				var ret = handler.apply( this, args );
-
-				if ( val !== false )
-					val = ret;
-
-				if ( ret === false ) {
-					event.preventDefault();
-					event.stopPropagation();
-				}
-			}
-		}
-
-		// Clean up added properties in IE to prevent memory leak
-		if (jQuery.browser.msie)
-			event.target = event.preventDefault = event.stopPropagation =
-				event.handler = event.data = null;
-
-		return val;
-	},
-
-	fix: function(event) {
-		// store a copy of the original event object 
-		// and clone to set read-only properties
-		var originalEvent = event;
-		event = jQuery.extend({}, originalEvent);
-		
-		// add preventDefault and stopPropagation since 
-		// they will not work on the clone
-		event.preventDefault = function() {
-			// if preventDefault exists run it on the original event
-			if (originalEvent.preventDefault)
-				originalEvent.preventDefault();
-			// otherwise set the returnValue property of the original event to false (IE)
-			originalEvent.returnValue = false;
-		};
-		event.stopPropagation = function() {
-			// if stopPropagation exists run it on the original event
-			if (originalEvent.stopPropagation)
-				originalEvent.stopPropagation();
-			// otherwise set the cancelBubble property of the original event to true (IE)
-			originalEvent.cancelBubble = true;
-		};
-		
-		// Fix target property, if necessary
-		if ( !event.target )
-			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
-				
-		// check if target is a textnode (safari)
-		if ( event.target.nodeType == 3 )
-			event.target = originalEvent.target.parentNode;
-
-		// Add relatedTarget, if necessary
-		if ( !event.relatedTarget && event.fromElement )
-			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
-
-		// Calculate pageX/Y if missing and clientX/Y available
-		if ( event.pageX == null && event.clientX != null ) {
-			var doc = document.documentElement, body = document.body;
-			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
-			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
-		}
-			
-		// Add which for key events
-		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
-			event.which = event.charCode || event.keyCode;
-		
-		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
-		if ( !event.metaKey && event.ctrlKey )
-			event.metaKey = event.ctrlKey;
-
-		// Add which for click: 1 == left; 2 == middle; 3 == right
-		// Note: button is not normalized, so don't use it
-		if ( !event.which && event.button )
-			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
-			
-		return event;
-	},
-	
-	special: {
-		ready: {
-			setup: function() {
-				// Make sure the ready event is setup
-				bindReady();
-				return;
-			},
-			
-			teardown: function() { return; }
-		},
-		
-		mouseenter: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseenter
-				arguments[0].type = "mouseenter";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		},
-	
-		mouseleave: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseleave
-				arguments[0].type = "mouseleave";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		}
-	}
-};
-
-jQuery.fn.extend({
-	bind: function( type, data, fn ) {
-		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
-			jQuery.event.add( this, type, fn || data, fn && data );
-		});
-	},
-	
-	one: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.add( this, type, function(event) {
-				jQuery(this).unbind(event);
-				return (fn || data).apply( this, arguments);
-			}, fn && data);
-		});
-	},
-
-	unbind: function( type, fn ) {
-		return this.each(function(){
-			jQuery.event.remove( this, type, fn );
-		});
-	},
-
-	trigger: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.trigger( type, data, this, true, fn );
-		});
-	},
-
-	triggerHandler: function( type, data, fn ) {
-		if ( this[0] )
-			return jQuery.event.trigger( type, data, this[0], false, fn );
-		return undefined;
-	},
-
-	toggle: function() {
-		// Save reference to arguments for access in closure
-		var args = arguments;
-
-		return this.click(function(event) {
-			// Figure out which function to execute
-			this.lastToggle = 0 == this.lastToggle ? 1 : 0;
-			
-			// Make sure that clicks stop
-			event.preventDefault();
-			
-			// and execute the function
-			return args[this.lastToggle].apply( this, arguments ) || false;
-		});
-	},
-
-	hover: function(fnOver, fnOut) {
-		return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
-	},
-	
-	ready: function(fn) {
-		// Attach the listeners
-		bindReady();
-
-		// If the DOM is already ready
-		if ( jQuery.isReady )
-			// Execute the function immediately
-			fn.call( document, jQuery );
-			
-		// Otherwise, remember the function for later
-		else
-			// Add the function to the wait list
-			jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
-	
-		return this;
-	}
-});
-
-jQuery.extend({
-	isReady: false,
-	readyList: [],
-	// Handle when the DOM is ready
-	ready: function() {
-		// Make sure that the DOM is not already loaded
-		if ( !jQuery.isReady ) {
-			// Remember that the DOM is ready
-			jQuery.isReady = true;
-			
-			// If there are functions bound, to execute
-			if ( jQuery.readyList ) {
-				// Execute all of them
-				jQuery.each( jQuery.readyList, function(){
-					this.apply( document );
-				});
-				
-				// Reset the list of functions
-				jQuery.readyList = null;
-			}
-		
-			// Trigger any bound ready events
-			jQuery(document).triggerHandler("ready");
-		}
-	}
-});
-
-var readyBound = false;
-
-function bindReady(){
-	if ( readyBound ) return;
-	readyBound = true;
-
-	// Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
-	if ( document.addEventListener && !jQuery.browser.opera)
-		// Use the handy event callback
-		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
-	
-	// If IE is used and is not in a frame
-	// Continually check to see if the document is ready
-	if ( jQuery.browser.msie && window == top ) (function(){
-		if (jQuery.isReady) return;
-		try {
-			// If IE is used, use the trick by Diego Perini
-			// http://javascript.nwbox.com/IEContentLoaded/
-			document.documentElement.doScroll("left");
-		} catch( error ) {
-			setTimeout( arguments.callee, 0 );
-			return;
-		}
-		// and execute any waiting functions
-		jQuery.ready();
-	})();
-
-	if ( jQuery.browser.opera )
-		document.addEventListener( "DOMContentLoaded", function () {
-			if (jQuery.isReady) return;
-			for (var i = 0; i < document.styleSheets.length; i++)
-				if (document.styleSheets[i].disabled) {
-					setTimeout( arguments.callee, 0 );
-					return;
-				}
-			// and execute any waiting functions
-			jQuery.ready();
-		}, false);
-
-	if ( jQuery.browser.safari ) {
-		var numStyles;
-		(function(){
-			if (jQuery.isReady) return;
-			if ( document.readyState != "loaded" && document.readyState != "complete" ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			if ( numStyles === undefined )
-				numStyles = jQuery("style, link[rel=stylesheet]").length;
-			if ( document.styleSheets.length != numStyles ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			// and execute any waiting functions
-			jQuery.ready();
-		})();
-	}
-
-	// A fallback to window.onload, that will always work
-	jQuery.event.add( window, "load", jQuery.ready );
-}
-
-jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
-	"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
-	"submit,keydown,keypress,keyup,error").split(","), function(i, name){
-	
-	// Handle event binding
-	jQuery.fn[name] = function(fn){
-		return fn ? this.bind(name, fn) : this.trigger(name);
-	};
-});
-
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function(event, elem) {
-	// Check if mouse(over|out) are still within the same parent element
-	var parent = event.relatedTarget;
-	// Traverse up the tree
-	while ( parent && parent != elem ) try { parent = parent.parentNode; } catch(error) { parent = elem; }
-	// Return true if we actually just moused on to a sub-element
-	return parent == elem;
-};
-
-// Prevent memory leaks in IE
-// And prevent errors on refresh with events like mouseover in other browsers
-// Window isn't included so as not to unbind existing unload events
-jQuery(window).bind("unload", function() {
-	jQuery("*").add(document).unbind();
-});
-jQuery.fn.extend({
-	load: function( url, params, callback ) {
-		if ( jQuery.isFunction( url ) )
-			return this.bind("load", url);
-
-		var off = url.indexOf(" ");
-		if ( off >= 0 ) {
-			var selector = url.slice(off, url.length);
-			url = url.slice(0, off);
-		}
-
-		callback = callback || function(){};
-
-		// Default to a GET request
-		var type = "GET";
-
-		// If the second parameter was provided
-		if ( params )
-			// If it's a function
-			if ( jQuery.isFunction( params ) ) {
-				// We assume that it's the callback
-				callback = params;
-				params = null;
-
-			// Otherwise, build a param string
-			} else {
-				params = jQuery.param( params );
-				type = "POST";
-			}
-
-		var self = this;
-
-		// Request the remote document
-		jQuery.ajax({
-			url: url,
-			type: type,
-			dataType: "html",
-			data: params,
-			complete: function(res, status){
-				// If successful, inject the HTML into all the matched elements
-				if ( status == "success" || status == "notmodified" )
-					// See if a selector was specified
-					self.html( selector ?
-						// Create a dummy div to hold the results
-						jQuery("<div/>")
-							// inject the contents of the document in, removing the scripts
-							// to avoid any 'Permission Denied' errors in IE
-							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
-
-							// Locate the specified elements
-							.find(selector) :
-
-						// If not, just inject the full result
-						res.responseText );
-
-				self.each( callback, [res.responseText, status, res] );
-			}
-		});
-		return this;
-	},
-
-	serialize: function() {
-		return jQuery.param(this.serializeArray());
-	},
-	serializeArray: function() {
-		return this.map(function(){
-			return jQuery.nodeName(this, "form") ?
-				jQuery.makeArray(this.elements) : this;
-		})
-		.filter(function(){
-			return this.name && !this.disabled && 
-				(this.checked || /select|textarea/i.test(this.nodeName) || 
-					/text|hidden|password/i.test(this.type));
-		})
-		.map(function(i, elem){
-			var val = jQuery(this).val();
-			return val == null ? null :
-				val.constructor == Array ?
-					jQuery.map( val, function(val, i){
-						return {name: elem.name, value: val};
-					}) :
-					{name: elem.name, value: val};
-		}).get();
-	}
-});
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
-	jQuery.fn[o] = function(f){
-		return this.bind(o, f);
-	};
-});
-
-var jsc = (new Date).getTime();
-
-jQuery.extend({
-	get: function( url, data, callback, type ) {
-		// shift arguments if data argument was ommited
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = null;
-		}
-		
-		return jQuery.ajax({
-			type: "GET",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	getScript: function( url, callback ) {
-		return jQuery.get(url, null, callback, "script");
-	},
-
-	getJSON: function( url, data, callback ) {
-		return jQuery.get(url, data, callback, "json");
-	},
-
-	post: function( url, data, callback, type ) {
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = {};
-		}
-
-		return jQuery.ajax({
-			type: "POST",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	ajaxSetup: function( settings ) {
-		jQuery.extend( jQuery.ajaxSettings, settings );
-	},
-
-	ajaxSettings: {
-		global: true,
-		type: "GET",
-		timeout: 0,
-		contentType: "application/x-www-form-urlencoded",
-		processData: true,
-		async: true,
-		data: null,
-		username: null,
-		password: null,
-		accepts: {
-			xml: "application/xml, text/xml",
-			html: "text/html",
-			script: "text/javascript, application/javascript",
-			json: "application/json, text/javascript",
-			text: "text/plain",
-			_default: "*/*"
-		}
-	},
-	
-	// Last-Modified header cache for next request
-	lastModified: {},
-
-	ajax: function( s ) {
-		var jsonp, jsre = /=\?(&|$)/g, status, data;
-
-		// Extend the settings, but re-extend 's' so that it can be
-		// checked again later (in the test suite, specifically)
-		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
-
-		// convert data if not already a string
-		if ( s.data && s.processData && typeof s.data != "string" )
-			s.data = jQuery.param(s.data);
-
-		// Handle JSONP Parameter Callbacks
-		if ( s.dataType == "jsonp" ) {
-			if ( s.type.toLowerCase() == "get" ) {
-				if ( !s.url.match(jsre) )
-					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
-			} else if ( !s.data || !s.data.match(jsre) )
-				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
-			s.dataType = "json";
-		}
-
-		// Build temporary JSONP function
-		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
-			jsonp = "jsonp" + jsc++;
-
-			// Replace the =? sequence both in the query string and the data
-			if ( s.data )
-				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
-			s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
-			// We need to make sure
-			// that a JSONP style response is executed properly
-			s.dataType = "script";
-
-			// Handle JSONP-style loading
-			window[ jsonp ] = function(tmp){
-				data = tmp;
-				success();
-				complete();
-				// Garbage collect
-				window[ jsonp ] = undefined;
-				try{ delete window[ jsonp ]; } catch(e){}
-				if ( head )
-					head.removeChild( script );
-			};
-		}
-
-		if ( s.dataType == "script" && s.cache == null )
-			s.cache = false;
-
-		if ( s.cache === false && s.type.toLowerCase() == "get" ) {
-			var ts = (new Date()).getTime();
-			// try replacing _= if it is there
-			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
-			// if nothing was replaced, add timestamp to the end
-			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
-		}
-
-		// If data is available, append data to url for get requests
-		if ( s.data && s.type.toLowerCase() == "get" ) {
-			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
-
-			// IE likes to send both get and post data, prevent this
-			s.data = null;
-		}
-
-		// Watch for a new set of requests
-		if ( s.global && ! jQuery.active++ )
-			jQuery.event.trigger( "ajaxStart" );
-
-		// If we're requesting a remote document
-		// and trying to load JSON or Script with a GET
-		if ( (!s.url.indexOf("http") || !s.url.indexOf("//")) && s.dataType == "script" && s.type.toLowerCase() == "get" ) {
-			var head = document.getElementsByTagName("head")[0];
-			var script = document.createElement("script");
-			script.src = s.url;
-			if (s.scriptCharset)
-				script.charset = s.scriptCharset;
-
-			// Handle Script loading
-			if ( !jsonp ) {
-				var done = false;
-
-				// Attach handlers for all browsers
-				script.onload = script.onreadystatechange = function(){
-					if ( !done && (!this.readyState || 
-							this.readyState == "loaded" || this.readyState == "complete") ) {
-						done = true;
-						success();
-						complete();
-						head.removeChild( script );
-					}
-				};
-			}
-
-			head.appendChild(script);
-
-			// We handle everything using the script element injection
-			return undefined;
-		}
-
-		var requestDone = false;
-
-		// Create the request object; Microsoft failed to properly
-		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
-		var xml = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
-
-		// Open the socket
-		xml.open(s.type, s.url, s.async, s.username, s.password);
-
-		// Need an extra try/catch for cross domain requests in Firefox 3
-		try {
-			// Set the correct header, if data is being sent
-			if ( s.data )
-				xml.setRequestHeader("Content-Type", s.contentType);
-
-			// Set the If-Modified-Since header, if ifModified mode.
-			if ( s.ifModified )
-				xml.setRequestHeader("If-Modified-Since",
-					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
-
-			// Set header so the called script knows that it's an XMLHttpRequest
-			xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-
-			// Set the Accepts header for the server, depending on the dataType
-			xml.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
-				s.accepts[ s.dataType ] + ", */*" :
-				s.accepts._default );
-		} catch(e){}
-
-		// Allow custom headers/mimetypes
-		if ( s.beforeSend )
-			s.beforeSend(xml);
-			
-		if ( s.global )
-			jQuery.event.trigger("ajaxSend", [xml, s]);
-
-		// Wait for a response to come back
-		var onreadystatechange = function(isTimeout){
-			// The transfer is complete and the data is available, or the request timed out
-			if ( !requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
-				requestDone = true;
-				
-				// clear poll interval
-				if (ival) {
-					clearInterval(ival);
-					ival = null;
-				}
-				
-				status = isTimeout == "timeout" && "timeout" ||
-					!jQuery.httpSuccess( xml ) && "error" ||
-					s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
-					"success";
-
-				if ( status == "success" ) {
-					// Watch for, and catch, XML document parse errors
-					try {
-						// process the data (runs the xml through httpData regardless of callback)
-						data = jQuery.httpData( xml, s.dataType );
-					} catch(e) {
-						status = "parsererror";
-					}
-				}
-
-				// Make sure that the request was successful or notmodified
-				if ( status == "success" ) {
-					// Cache Last-Modified header, if ifModified mode.
-					var modRes;
-					try {
-						modRes = xml.getResponseHeader("Last-Modified");
-					} catch(e) {} // swallow exception thrown by FF if header is not available
-	
-					if ( s.ifModified && modRes )
-						jQuery.lastModified[s.url] = modRes;
-
-					// JSONP handles its own success callback
-					if ( !jsonp )
-						success();	
-				} else
-					jQuery.handleError(s, xml, status);
-
-				// Fire the complete handlers
-				complete();
-
-				// Stop memory leaks
-				if ( s.async )
-					xml = null;
-			}
-		};
-		
-		if ( s.async ) {
-			// don't attach the handler to the request, just poll it instead
-			var ival = setInterval(onreadystatechange, 13); 
-
-			// Timeout checker
-			if ( s.timeout > 0 )
-				setTimeout(function(){
-					// Check to see if the request is still happening
-					if ( xml ) {
-						// Cancel the request
-						xml.abort();
-	
-						if( !requestDone )
-							onreadystatechange( "timeout" );
-					}
-				}, s.timeout);
-		}
-			
-		// Send the data
-		try {
-			xml.send(s.data);
-		} catch(e) {
-			jQuery.handleError(s, xml, null, e);
-		}
-		
-		// firefox 1.5 doesn't fire statechange for sync requests
-		if ( !s.async )
-			onreadystatechange();
-
-		function success(){
-			// If a local callback was specified, fire it and pass it the data
-			if ( s.success )
-				s.success( data, status );
-
-			// Fire the global callback
-			if ( s.global )
-				jQuery.event.trigger( "ajaxSuccess", [xml, s] );
-		}
-
-		function complete(){
-			// Process result
-			if ( s.complete )
-				s.complete(xml, status);
-
-			// The request was completed
-			if ( s.global )
-				jQuery.event.trigger( "ajaxComplete", [xml, s] );
-
-			// Handle the global AJAX counter
-			if ( s.global && ! --jQuery.active )
-				jQuery.event.trigger( "ajaxStop" );
-		}
-		
-		// return XMLHttpRequest to allow aborting the request etc.
-		return xml;
-	},
-
-	handleError: function( s, xml, status, e ) {
-		// If a local callback was specified, fire it
-		if ( s.error ) s.error( xml, status, e );
-
-		// Fire the global callback
-		if ( s.global )
-			jQuery.event.trigger( "ajaxError", [xml, s, e] );
-	},
-
-	// Counter for holding the number of active queries
-	active: 0,
-
-	// Determines if an XMLHttpRequest was successful or not
-	httpSuccess: function( r ) {
-		try {
-			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
-			return !r.status && location.protocol == "file:" ||
-				( r.status >= 200 && r.status < 300 ) || r.status == 304 || r.status == 1223 ||
-				jQuery.browser.safari && r.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	// Determines if an XMLHttpRequest returns NotModified
-	httpNotModified: function( xml, url ) {
-		try {
-			var xmlRes = xml.getResponseHeader("Last-Modified");
-
-			// Firefox always returns 200. check Last-Modified date
-			return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
-				jQuery.browser.safari && xml.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	httpData: function( r, type ) {
-		var ct = r.getResponseHeader("content-type");
-		var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
-		var data = xml ? r.responseXML : r.responseText;
-
-		if ( xml && data.documentElement.tagName == "parsererror" )
-			throw "parsererror";
-
-		// If the type is "script", eval it in global context
-		if ( type == "script" )
-			jQuery.globalEval( data );
-
-		// Get the JavaScript object, if JSON is used.
-		if ( type == "json" )
-			data = eval("(" + data + ")");
-
-		return data;
-	},
-
-	// Serialize an array of form elements or a set of
-	// key/values into a query string
-	param: function( a ) {
-		var s = [];
-
-		// If an array was passed in, assume that it is an array
-		// of form elements
-		if ( a.constructor == Array || a.jquery )
-			// Serialize the form elements
-			jQuery.each( a, function(){
-				s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
-			});
-
-		// Otherwise, assume that it's an object of key/value pairs
-		else
-			// Serialize the key/values
-			for ( var j in a )
-				// If the value is an array then the key names need to be repeated
-				if ( a[j] && a[j].constructor == Array )
-					jQuery.each( a[j], function(){
-						s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
-					});
-				else
-					s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
-
-		// Return the resulting serialization
-		return s.join("&").replace(/%20/g, "+");
-	}
-
-});
-jQuery.fn.extend({
-	show: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "show", width: "show", opacity: "show"
-			}, speed, callback) :
-			
-			this.filter(":hidden").each(function(){
-				this.style.display = this.oldblock || "";
-				if ( jQuery.css(this,"display") == "none" ) {
-					var elem = jQuery("<" + this.tagName + " />").appendTo("body");
-					this.style.display = elem.css("display");
-					// handle an edge condition where css is - div { display:none; } or similar
-					if (this.style.display == "none")
-						this.style.display = "block";
-					elem.remove();
-				}
-			}).end();
-	},
-	
-	hide: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "hide", width: "hide", opacity: "hide"
-			}, speed, callback) :
-			
-			this.filter(":visible").each(function(){
-				this.oldblock = this.oldblock || jQuery.css(this,"display");
-				this.style.display = "none";
-			}).end();
-	},
-
-	// Save the old toggle function
-	_toggle: jQuery.fn.toggle,
-	
-	toggle: function( fn, fn2 ){
-		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
-			this._toggle( fn, fn2 ) :
-			fn ?
-				this.animate({
-					height: "toggle", width: "toggle", opacity: "toggle"
-				}, fn, fn2) :
-				this.each(function(){
-					jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
-				});
-	},
-	
-	slideDown: function(speed,callback){
-		return this.animate({height: "show"}, speed, callback);
-	},
-	
-	slideUp: function(speed,callback){
-		return this.animate({height: "hide"}, speed, callback);
-	},
-
-	slideToggle: function(speed, callback){
-		return this.animate({height: "toggle"}, speed, callback);
-	},
-	
-	fadeIn: function(speed, callback){
-		return this.animate({opacity: "show"}, speed, callback);
-	},
-	
-	fadeOut: function(speed, callback){
-		return this.animate({opacity: "hide"}, speed, callback);
-	},
-	
-	fadeTo: function(speed,to,callback){
-		return this.animate({opacity: to}, speed, callback);
-	},
-	
-	animate: function( prop, speed, easing, callback ) {
-		var optall = jQuery.speed(speed, easing, callback);
-
-		return this[ optall.queue === false ? "each" : "queue" ](function(){
-			if ( this.nodeType != 1)
-				return false;
-
-			var opt = jQuery.extend({}, optall);
-			var hidden = jQuery(this).is(":hidden"), self = this;
-			
-			for ( var p in prop ) {
-				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
-					return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
-
-				if ( p == "height" || p == "width" ) {
-					// Store display property
-					opt.display = jQuery.css(this, "display");
-
-					// Make sure that nothing sneaks out
-					opt.overflow = this.style.overflow;
-				}
-			}
-
-			if ( opt.overflow != null )
-				this.style.overflow = "hidden";
-
-			opt.curAnim = jQuery.extend({}, prop);
-			
-			jQuery.each( prop, function(name, val){
-				var e = new jQuery.fx( self, opt, name );
-
-				if ( /toggle|show|hide/.test(val) )
-					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
-				else {
-					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
-						start = e.cur(true) || 0;
-
-					if ( parts ) {
-						var end = parseFloat(parts[2]),
-							unit = parts[3] || "px";
-
-						// We need to compute starting value
-						if ( unit != "px" ) {
-							self.style[ name ] = (end || 1) + unit;
-							start = ((end || 1) / e.cur(true)) * start;
-							self.style[ name ] = start + unit;
-						}
-
-						// If a +=/-= token was provided, we're doing a relative animation
-						if ( parts[1] )
-							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
-
-						e.custom( start, end, unit );
-					} else
-						e.custom( start, val, "" );
-				}
-			});
-
-			// For JS strict compliance
-			return true;
-		});
-	},
-	
-	queue: function(type, fn){
-		if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
-			fn = type;
-			type = "fx";
-		}
-
-		if ( !type || (typeof type == "string" && !fn) )
-			return queue( this[0], type );
-
-		return this.each(function(){
-			if ( fn.constructor == Array )
-				queue(this, type, fn);
-			else {
-				queue(this, type).push( fn );
-			
-				if ( queue(this, type).length == 1 )
-					fn.apply(this);
-			}
-		});
-	},
-
-	stop: function(clearQueue, gotoEnd){
-		var timers = jQuery.timers;
-
-		if (clearQueue)
-			this.queue([]);
-
-		this.each(function(){
-			// go in reverse order so anything added to the queue during the loop is ignored
-			for ( var i = timers.length - 1; i >= 0; i-- )
-				if ( timers[i].elem == this ) {
-					if (gotoEnd)
-						// force the next step to be the last
-						timers[i](true);
-					timers.splice(i, 1);
-				}
-		});
-
-		// start the next in the queue if the last step wasn't forced
-		if (!gotoEnd)
-			this.dequeue();
-
-		return this;
-	}
-
-});
-
-var queue = function( elem, type, array ) {
-	if ( !elem )
-		return undefined;
-
-	type = type || "fx";
-
-	var q = jQuery.data( elem, type + "queue" );
-
-	if ( !q || array )
-		q = jQuery.data( elem, type + "queue", 
-			array ? jQuery.makeArray(array) : [] );
-
-	return q;
-};
-
-jQuery.fn.dequeue = function(type){
-	type = type || "fx";
-
-	return this.each(function(){
-		var q = queue(this, type);
-
-		q.shift();
-
-		if ( q.length )
-			q[0].apply( this );
-	});
-};
-
-jQuery.extend({
-	
-	speed: function(speed, easing, fn) {
-		var opt = speed && speed.constructor == Object ? speed : {
-			complete: fn || !fn && easing || 
-				jQuery.isFunction( speed ) && speed,
-			duration: speed,
-			easing: fn && easing || easing && easing.constructor != Function && easing
-		};
-
-		opt.duration = (opt.duration && opt.duration.constructor == Number ? 
-			opt.duration : 
-			{ slow: 600, fast: 200 }[opt.duration]) || 400;
-	
-		// Queueing
-		opt.old = opt.complete;
-		opt.complete = function(){
-			if ( opt.queue !== false )
-				jQuery(this).dequeue();
-			if ( jQuery.isFunction( opt.old ) )
-				opt.old.apply( this );
-		};
-	
-		return opt;
-	},
-	
-	easing: {
-		linear: function( p, n, firstNum, diff ) {
-			return firstNum + diff * p;
-		},
-		swing: function( p, n, firstNum, diff ) {
-			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
-		}
-	},
-	
-	timers: [],
-	timerId: null,
-
-	fx: function( elem, options, prop ){
-		this.options = options;
-		this.elem = elem;
-		this.prop = prop;
-
-		if ( !options.orig )
-			options.orig = {};
-	}
-
-});
-
-jQuery.fx.prototype = {
-
-	// Simple function for setting a style value
-	update: function(){
-		if ( this.options.step )
-			this.options.step.apply( this.elem, [ this.now, this ] );
-
-		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
-
-		// Set display property to block for height/width animations
-		if ( this.prop == "height" || this.prop == "width" )
-			this.elem.style.display = "block";
-	},
-
-	// Get the current size
-	cur: function(force){
-		if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
-			return this.elem[ this.prop ];
-
-		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
-		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
-	},
-
-	// Start an animation from one number to another
-	custom: function(from, to, unit){
-		this.startTime = (new Date()).getTime();
-		this.start = from;
-		this.end = to;
-		this.unit = unit || this.unit || "px";
-		this.now = this.start;
-		this.pos = this.state = 0;
-		this.update();
-
-		var self = this;
-		function t(gotoEnd){
-			return self.step(gotoEnd);
-		}
-
-		t.elem = this.elem;
-
-		jQuery.timers.push(t);
-
-		if ( jQuery.timerId == null ) {
-			jQuery.timerId = setInterval(function(){
-				var timers = jQuery.timers;
-				
-				for ( var i = 0; i < timers.length; i++ )
-					if ( !timers[i]() )
-						timers.splice(i--, 1);
-
-				if ( !timers.length ) {
-					clearInterval( jQuery.timerId );
-					jQuery.timerId = null;
-				}
-			}, 13);
-		}
-	},
-
-	// Simple 'show' function
-	show: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.show = true;
-
-		// Begin the animation
-		this.custom(0, this.cur());
-
-		// Make sure that we start at a small width/height to avoid any
-		// flash of content
-		if ( this.prop == "width" || this.prop == "height" )
-			this.elem.style[this.prop] = "1px";
-		
-		// Start by showing the element
-		jQuery(this.elem).show();
-	},
-
-	// Simple 'hide' function
-	hide: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.hide = true;
-
-		// Begin the animation
-		this.custom(this.cur(), 0);
-	},
-
-	// Each step of an animation
-	step: function(gotoEnd){
-		var t = (new Date()).getTime();
-
-		if ( gotoEnd || t > this.options.duration + this.startTime ) {
-			this.now = this.end;
-			this.pos = this.state = 1;
-			this.update();
-
-			this.options.curAnim[ this.prop ] = true;
-
-			var done = true;
-			for ( var i in this.options.curAnim )
-				if ( this.options.curAnim[i] !== true )
-					done = false;
-
-			if ( done ) {
-				if ( this.options.display != null ) {
-					// Reset the overflow
-					this.elem.style.overflow = this.options.overflow;
-				
-					// Reset the display
-					this.elem.style.display = this.options.display;
-					if ( jQuery.css(this.elem, "display") == "none" )
-						this.elem.style.display = "block";
-				}
-
-				// Hide the element if the "hide" operation was done
-				if ( this.options.hide )
-					this.elem.style.display = "none";
-
-				// Reset the properties, if the item has been hidden or shown
-				if ( this.options.hide || this.options.show )
-					for ( var p in this.options.curAnim )
-						jQuery.attr(this.elem.style, p, this.options.orig[p]);
-			}
-
-			// If a callback was provided, execute it
-			if ( done && jQuery.isFunction( this.options.complete ) )
-				// Execute the complete function
-				this.options.complete.apply( this.elem );
-
-			return false;
-		} else {
-			var n = t - this.startTime;
-			this.state = n / this.options.duration;
-
-			// Perform the easing function, defaults to swing
-			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
-			this.now = this.start + ((this.end - this.start) * this.pos);
-
-			// Perform the next step of the animation
-			this.update();
-		}
-
-		return true;
-	}
-
-};
-
-jQuery.fx.step = {
-	scrollLeft: function(fx){
-		fx.elem.scrollLeft = fx.now;
-	},
-
-	scrollTop: function(fx){
-		fx.elem.scrollTop = fx.now;
-	},
-
-	opacity: function(fx){
-		jQuery.attr(fx.elem.style, "opacity", fx.now);
-	},
-
-	_default: function(fx){
-		fx.elem.style[ fx.prop ] = fx.now + fx.unit;
-	}
-};
-// The Offset Method
-// Originally By Brandon Aaron, part of the Dimension Plugin
-// http://jquery.com/plugins/project/dimensions
-jQuery.fn.offset = function() {
-	var left = 0, top = 0, elem = this[0], results;
-	
-	if ( elem ) with ( jQuery.browser ) {
-		var parent       = elem.parentNode, 
-		    offsetChild  = elem,
-		    offsetParent = elem.offsetParent, 
-		    doc          = elem.ownerDocument,
-		    safari2      = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
-		    fixed        = jQuery.css(elem, "position") == "fixed";
-	
-		// Use getBoundingClientRect if available
-		if ( elem.getBoundingClientRect ) {
-			var box = elem.getBoundingClientRect();
-		
-			// Add the document scroll offsets
-			add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-				box.top  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		
-			// IE adds the HTML element's border, by default it is medium which is 2px
-			// IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; }
-			// IE 7 standards mode, the border is always 2px
-			// This border/offset is typically represented by the clientLeft and clientTop properties
-			// However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
-			// Therefore this method will be off by 2px in IE while in quirksmode
-			add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
-	
-		// Otherwise loop through the offsetParents and parentNodes
-		} else {
-		
-			// Initial element offsets
-			add( elem.offsetLeft, elem.offsetTop );
-			
-			// Get parent offsets
-			while ( offsetParent ) {
-				// Add offsetParent offsets
-				add( offsetParent.offsetLeft, offsetParent.offsetTop );
-			
-				// Mozilla and Safari > 2 does not include the border on offset parents
-				// However Mozilla adds the border for table or table cells
-				if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
-					border( offsetParent );
-					
-				// Add the document scroll offsets if position is fixed on any offsetParent
-				if ( !fixed && jQuery.css(offsetParent, "position") == "fixed" )
-					fixed = true;
-			
-				// Set offsetChild to previous offsetParent unless it is the body element
-				offsetChild  = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
-				// Get next offsetParent
-				offsetParent = offsetParent.offsetParent;
-			}
-		
-			// Get parent scroll offsets
-			while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) ) {
-				// Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
-				if ( !/^inline|table.*$/i.test(jQuery.css(parent, "display")) )
-					// Subtract parent scroll offsets
-					add( -parent.scrollLeft, -parent.scrollTop );
-			
-				// Mozilla does not add the border for a parent that has overflow != visible
-				if ( mozilla && jQuery.css(parent, "overflow") != "visible" )
-					border( parent );
-			
-				// Get next parent
-				parent = parent.parentNode;
-			}
-		
-			// Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
-			// Mozilla doubles body offsets with a non-absolutely positioned offsetChild
-			if ( (safari2 && (fixed || jQuery.css(offsetChild, "position") == "absolute")) || 
-				(mozilla && jQuery.css(offsetChild, "position") != "absolute") )
-					add( -doc.body.offsetLeft, -doc.body.offsetTop );
-			
-			// Add the document scroll offsets if position is fixed
-			if ( fixed )
-				add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-					Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		}
-
-		// Return an object with top and left properties
-		results = { top: top, left: left };
-	}
-
-	function border(elem) {
-		add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) );
-	}
-
-	function add(l, t) {
-		left += parseInt(l) || 0;
-		top += parseInt(t) || 0;
-	}
-
-	return results;
-};
-})();
@@ -1,3408 +0,0 @@
-(function(){
-/*
- * jQuery 1.2.3 - New Wave Javascript
- *
- * Copyright (c) 2008 John Resig (jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
- * $Rev: 4663 $
- */
-
-// Map over jQuery in case of overwrite
-if ( window.jQuery )
-	var _jQuery = window.jQuery;
-
-var jQuery = window.jQuery = function( selector, context ) {
-	// The jQuery object is actually just the init constructor 'enhanced'
-	return new jQuery.prototype.init( selector, context );
-};
-
-// Map over the $ in case of overwrite
-if ( window.$ )
-	var _$ = window.$;
-	
-// Map the jQuery namespace to the '$' one
-window.$ = jQuery;
-
-// A simple way to check for HTML strings or ID strings
-// (both of which we optimize for)
-var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
-
-// Is it a simple selector
-var isSimple = /^.[^:#\[\.]*$/;
-
-jQuery.fn = jQuery.prototype = {
-	init: function( selector, context ) {
-		// Make sure that a selection was provided
-		selector = selector || document;
-
-		// Handle $(DOMElement)
-		if ( selector.nodeType ) {
-			this[0] = selector;
-			this.length = 1;
-			return this;
-
-		// Handle HTML strings
-		} else if ( typeof selector == "string" ) {
-			// Are we dealing with HTML string or an ID?
-			var match = quickExpr.exec( selector );
-
-			// Verify a match, and that no context was specified for #id
-			if ( match && (match[1] || !context) ) {
-
-				// HANDLE: $(html) -> $(array)
-				if ( match[1] )
-					selector = jQuery.clean( [ match[1] ], context );
-
-				// HANDLE: $("#id")
-				else {
-					var elem = document.getElementById( match[3] );
-
-					// Make sure an element was located
-					if ( elem )
-						// Handle the case where IE and Opera return items
-						// by name instead of ID
-						if ( elem.id != match[3] )
-							return jQuery().find( selector );
-
-						// Otherwise, we inject the element directly into the jQuery object
-						else {
-							this[0] = elem;
-							this.length = 1;
-							return this;
-						}
-
-					else
-						selector = [];
-				}
-
-			// HANDLE: $(expr, [context])
-			// (which is just equivalent to: $(content).find(expr)
-			} else
-				return new jQuery( context ).find( selector );
-
-		// HANDLE: $(function)
-		// Shortcut for document ready
-		} else if ( jQuery.isFunction( selector ) )
-			return new jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
-
-		return this.setArray(
-			// HANDLE: $(array)
-			selector.constructor == Array && selector ||
-
-			// HANDLE: $(arraylike)
-			// Watch for when an array-like object, contains DOM nodes, is passed in as the selector
-			(selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) ||
-
-			// HANDLE: $(*)
-			[ selector ] );
-	},
-	
-	// The current version of jQuery being used
-	jquery: "1.2.3",
-
-	// The number of elements contained in the matched element set
-	size: function() {
-		return this.length;
-	},
-	
-	// The number of elements contained in the matched element set
-	length: 0,
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-		return num == undefined ?
-
-			// Return a 'clean' array
-			jQuery.makeArray( this ) :
-
-			// Return just the object
-			this[ num ];
-	},
-	
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-		// Build a new jQuery matched element set
-		var ret = jQuery( elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-	
-	// Force the current matched set of elements to become
-	// the specified array of elements (destroying the stack in the process)
-	// You should use pushStack() in order to do this, but maintain the stack
-	setArray: function( elems ) {
-		// Resetting the length to 0, then using the native Array push
-		// is a super-fast way to populate an object with array-like properties
-		this.length = 0;
-		Array.prototype.push.apply( this, elems );
-		
-		return this;
-	},
-
-	// Execute a callback for every element in the matched set.
-	// (You can seed the arguments with an array of args, but this is
-	// only used internally.)
-	each: function( callback, args ) {
-		return jQuery.each( this, callback, args );
-	},
-
-	// Determine the position of an element within 
-	// the matched set of elements
-	index: function( elem ) {
-		var ret = -1;
-
-		// Locate the position of the desired element
-		this.each(function(i){
-			if ( this == elem )
-				ret = i;
-		});
-
-		return ret;
-	},
-
-	attr: function( name, value, type ) {
-		var options = name;
-		
-		// Look for the case where we're accessing a style value
-		if ( name.constructor == String )
-			if ( value == undefined )
-				return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined;
-
-			else {
-				options = {};
-				options[ name ] = value;
-			}
-		
-		// Check to see if we're setting style values
-		return this.each(function(i){
-			// Set all the styles
-			for ( name in options )
-				jQuery.attr(
-					type ?
-						this.style :
-						this,
-					name, jQuery.prop( this, options[ name ], type, i, name )
-				);
-		});
-	},
-
-	css: function( key, value ) {
-		// ignore negative width and height values
-		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
-			value = undefined;
-		return this.attr( key, value, "curCSS" );
-	},
-
-	text: function( text ) {
-		if ( typeof text != "object" && text != null )
-			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
-
-		var ret = "";
-
-		jQuery.each( text || this, function(){
-			jQuery.each( this.childNodes, function(){
-				if ( this.nodeType != 8 )
-					ret += this.nodeType != 1 ?
-						this.nodeValue :
-						jQuery.fn.text( [ this ] );
-			});
-		});
-
-		return ret;
-	},
-
-	wrapAll: function( html ) {
-		if ( this[0] )
-			// The elements to wrap the target around
-			jQuery( html, this[0].ownerDocument )
-				.clone()
-				.insertBefore( this[0] )
-				.map(function(){
-					var elem = this;
-
-					while ( elem.firstChild )
-						elem = elem.firstChild;
-
-					return elem;
-				})
-				.append(this);
-
-		return this;
-	},
-
-	wrapInner: function( html ) {
-		return this.each(function(){
-			jQuery( this ).contents().wrapAll( html );
-		});
-	},
-
-	wrap: function( html ) {
-		return this.each(function(){
-			jQuery( this ).wrapAll( html );
-		});
-	},
-
-	append: function() {
-		return this.domManip(arguments, true, false, function(elem){
-			if (this.nodeType == 1)
-				this.appendChild( elem );
-		});
-	},
-
-	prepend: function() {
-		return this.domManip(arguments, true, true, function(elem){
-			if (this.nodeType == 1)
-				this.insertBefore( elem, this.firstChild );
-		});
-	},
-	
-	before: function() {
-		return this.domManip(arguments, false, false, function(elem){
-			this.parentNode.insertBefore( elem, this );
-		});
-	},
-
-	after: function() {
-		return this.domManip(arguments, false, true, function(elem){
-			this.parentNode.insertBefore( elem, this.nextSibling );
-		});
-	},
-
-	end: function() {
-		return this.prevObject || jQuery( [] );
-	},
-
-	find: function( selector ) {
-		var elems = jQuery.map(this, function(elem){
-			return jQuery.find( selector, elem );
-		});
-
-		return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
-			jQuery.unique( elems ) :
-			elems );
-	},
-
-	clone: function( events ) {
-		// Do the clone
-		var ret = this.map(function(){
-			if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
-				// IE copies events bound via attachEvent when
-				// using cloneNode. Calling detachEvent on the
-				// clone will also remove the events from the orignal
-				// In order to get around this, we use innerHTML.
-				// Unfortunately, this means some modifications to 
-				// attributes in IE that are actually only stored 
-				// as properties will not be copied (such as the
-				// the name attribute on an input).
-				var clone = this.cloneNode(true),
-					container = document.createElement("div");
-				container.appendChild(clone);
-				return jQuery.clean([container.innerHTML])[0];
-			} else
-				return this.cloneNode(true);
-		});
-
-		// Need to set the expando to null on the cloned set if it exists
-		// removeData doesn't work here, IE removes it from the original as well
-		// this is primarily for IE but the data expando shouldn't be copied over in any browser
-		var clone = ret.find("*").andSelf().each(function(){
-			if ( this[ expando ] != undefined )
-				this[ expando ] = null;
-		});
-		
-		// Copy the events from the original to the clone
-		if ( events === true )
-			this.find("*").andSelf().each(function(i){
-				if (this.nodeType == 3)
-					return;
-				var events = jQuery.data( this, "events" );
-
-				for ( var type in events )
-					for ( var handler in events[ type ] )
-						jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
-			});
-
-		// Return the cloned set
-		return ret;
-	},
-
-	filter: function( selector ) {
-		return this.pushStack(
-			jQuery.isFunction( selector ) &&
-			jQuery.grep(this, function(elem, i){
-				return selector.call( elem, i );
-			}) ||
-
-			jQuery.multiFilter( selector, this ) );
-	},
-
-	not: function( selector ) {
-		if ( selector.constructor == String )
-			// test special case where just one selector is passed in
-			if ( isSimple.test( selector ) )
-				return this.pushStack( jQuery.multiFilter( selector, this, true ) );
-			else
-				selector = jQuery.multiFilter( selector, this );
-
-		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
-		return this.filter(function() {
-			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
-		});
-	},
-
-	add: function( selector ) {
-		return !selector ? this : this.pushStack( jQuery.merge( 
-			this.get(),
-			selector.constructor == String ? 
-				jQuery( selector ).get() :
-				selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ?
-					selector : [selector] ) );
-	},
-
-	is: function( selector ) {
-		return selector ?
-			jQuery.multiFilter( selector, this ).length > 0 :
-			false;
-	},
-
-	hasClass: function( selector ) {
-		return this.is( "." + selector );
-	},
-	
-	val: function( value ) {
-		if ( value == undefined ) {
-
-			if ( this.length ) {
-				var elem = this[0];
-
-				// We need to handle select boxes special
-				if ( jQuery.nodeName( elem, "select" ) ) {
-					var index = elem.selectedIndex,
-						values = [],
-						options = elem.options,
-						one = elem.type == "select-one";
-					
-					// Nothing was selected
-					if ( index < 0 )
-						return null;
-
-					// Loop through all the selected options
-					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
-						var option = options[ i ];
-
-						if ( option.selected ) {
-							// Get the specifc value for the option
-							value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
-							
-							// We don't need an array for one selects
-							if ( one )
-								return value;
-							
-							// Multi-Selects return an array
-							values.push( value );
-						}
-					}
-					
-					return values;
-					
-				// Everything else, we just grab the value
-				} else
-					return (this[0].value || "").replace(/\r/g, "");
-
-			}
-
-			return undefined;
-		}
-
-		return this.each(function(){
-			if ( this.nodeType != 1 )
-				return;
-
-			if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
-				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
-					jQuery.inArray(this.name, value) >= 0);
-
-			else if ( jQuery.nodeName( this, "select" ) ) {
-				var values = value.constructor == Array ?
-					value :
-					[ value ];
-
-				jQuery( "option", this ).each(function(){
-					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
-						jQuery.inArray( this.text, values ) >= 0);
-				});
-
-				if ( !values.length )
-					this.selectedIndex = -1;
-
-			} else
-				this.value = value;
-		});
-	},
-	
-	html: function( value ) {
-		return value == undefined ?
-			(this.length ?
-				this[0].innerHTML :
-				null) :
-			this.empty().append( value );
-	},
-
-	replaceWith: function( value ) {
-		return this.after( value ).remove();
-	},
-
-	eq: function( i ) {
-		return this.slice( i, i + 1 );
-	},
-
-	slice: function() {
-		return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map(this, function(elem, i){
-			return callback.call( elem, i, elem );
-		}));
-	},
-
-	andSelf: function() {
-		return this.add( this.prevObject );
-	},
-
-	data: function( key, value ){
-		var parts = key.split(".");
-		parts[1] = parts[1] ? "." + parts[1] : "";
-
-		if ( value == null ) {
-			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-			
-			if ( data == undefined && this.length )
-				data = jQuery.data( this[0], key );
-
-			return data == null && parts[1] ?
-				this.data( parts[0] ) :
-				data;
-		} else
-			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
-				jQuery.data( this, key, value );
-			});
-	},
-
-	removeData: function( key ){
-		return this.each(function(){
-			jQuery.removeData( this, key );
-		});
-	},
-	
-	domManip: function( args, table, reverse, callback ) {
-		var clone = this.length > 1, elems; 
-
-		return this.each(function(){
-			if ( !elems ) {
-				elems = jQuery.clean( args, this.ownerDocument );
-
-				if ( reverse )
-					elems.reverse();
-			}
-
-			var obj = this;
-
-			if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
-				obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
-
-			var scripts = jQuery( [] );
-
-			jQuery.each(elems, function(){
-				var elem = clone ?
-					jQuery( this ).clone( true )[0] :
-					this;
-
-				// execute all scripts after the elements have been injected
-				if ( jQuery.nodeName( elem, "script" ) ) {
-					scripts = scripts.add( elem );
-				} else {
-					// Remove any inner scripts for later evaluation
-					if ( elem.nodeType == 1 )
-						scripts = scripts.add( jQuery( "script", elem ).remove() );
-
-					// Inject the elements into the document
-					callback.call( obj, elem );
-				}
-			});
-
-			scripts.each( evalScript );
-		});
-	}
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.prototype.init.prototype = jQuery.prototype;
-
-function evalScript( i, elem ) {
-	if ( elem.src )
-		jQuery.ajax({
-			url: elem.src,
-			async: false,
-			dataType: "script"
-		});
-
-	else
-		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
-
-	if ( elem.parentNode )
-		elem.parentNode.removeChild( elem );
-}
-
-jQuery.extend = jQuery.fn.extend = function() {
-	// copy reference to target object
-	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
-
-	// Handle a deep copy situation
-	if ( target.constructor == Boolean ) {
-		deep = target;
-		target = arguments[1] || {};
-		// skip the boolean and the target
-		i = 2;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target != "object" && typeof target != "function" )
-		target = {};
-
-	// extend jQuery itself if only one argument is passed
-	if ( length == 1 ) {
-		target = this;
-		i = 0;
-	}
-
-	for ( ; i < length; i++ )
-		// Only deal with non-null/undefined values
-		if ( (options = arguments[ i ]) != null )
-			// Extend the base object
-			for ( var name in options ) {
-				// Prevent never-ending loop
-				if ( target === options[ name ] )
-					continue;
-
-				// Recurse if we're merging object values
-				if ( deep && options[ name ] && typeof options[ name ] == "object" && target[ name ] && !options[ name ].nodeType )
-					target[ name ] = jQuery.extend( target[ name ], options[ name ] );
-
-				// Don't bring in undefined values
-				else if ( options[ name ] != undefined )
-					target[ name ] = options[ name ];
-
-			}
-
-	// Return the modified object
-	return target;
-};
-
-var expando = "jQuery" + (new Date()).getTime(), uuid = 0, windowData = {};
-
-// exclude the following css properties to add px
-var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;
-
-jQuery.extend({
-	noConflict: function( deep ) {
-		window.$ = _$;
-
-		if ( deep )
-			window.jQuery = _jQuery;
-
-		return jQuery;
-	},
-
-	// See test/unit/core.js for details concerning this function.
-	isFunction: function( fn ) {
-		return !!fn && typeof fn != "string" && !fn.nodeName && 
-			fn.constructor != Array && /function/i.test( fn + "" );
-	},
-	
-	// check if an element is in a (or is an) XML document
-	isXMLDoc: function( elem ) {
-		return elem.documentElement && !elem.body ||
-			elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
-	},
-
-	// Evalulates a script in a global context
-	globalEval: function( data ) {
-		data = jQuery.trim( data );
-
-		if ( data ) {
-			// Inspired by code by Andrea Giammarchi
-			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
-			var head = document.getElementsByTagName("head")[0] || document.documentElement,
-				script = document.createElement("script");
-
-			script.type = "text/javascript";
-			if ( jQuery.browser.msie )
-				script.text = data;
-			else
-				script.appendChild( document.createTextNode( data ) );
-
-			head.appendChild( script );
-			head.removeChild( script );
-		}
-	},
-
-	nodeName: function( elem, name ) {
-		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
-	},
-	
-	cache: {},
-	
-	data: function( elem, name, data ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// Compute a unique ID for the element
-		if ( !id ) 
-			id = elem[ expando ] = ++uuid;
-
-		// Only generate the data cache if we're
-		// trying to access or manipulate it
-		if ( name && !jQuery.cache[ id ] )
-			jQuery.cache[ id ] = {};
-		
-		// Prevent overriding the named cache with undefined values
-		if ( data != undefined )
-			jQuery.cache[ id ][ name ] = data;
-		
-		// Return the named cache data, or the ID for the element	
-		return name ?
-			jQuery.cache[ id ][ name ] :
-			id;
-	},
-	
-	removeData: function( elem, name ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// If we want to remove a specific section of the element's data
-		if ( name ) {
-			if ( jQuery.cache[ id ] ) {
-				// Remove the section of cache data
-				delete jQuery.cache[ id ][ name ];
-
-				// If we've removed all the data, remove the element's cache
-				name = "";
-
-				for ( name in jQuery.cache[ id ] )
-					break;
-
-				if ( !name )
-					jQuery.removeData( elem );
-			}
-
-		// Otherwise, we want to remove all of the element's data
-		} else {
-			// Clean up the element expando
-			try {
-				delete elem[ expando ];
-			} catch(e){
-				// IE has trouble directly removing the expando
-				// but it's ok with using removeAttribute
-				if ( elem.removeAttribute )
-					elem.removeAttribute( expando );
-			}
-
-			// Completely remove the data cache
-			delete jQuery.cache[ id ];
-		}
-	},
-
-	// args is for internal usage only
-	each: function( object, callback, args ) {
-		if ( args ) {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.apply( object[ name ], args ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length; i < length; i++ )
-					if ( callback.apply( object[ i ], args ) === false )
-						break;
-
-		// A special, fast, case for the most common use of each
-		} else {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.call( object[ name ], name, object[ name ] ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length, value = object[0]; 
-					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
-		}
-
-		return object;
-	},
-	
-	prop: function( elem, value, type, i, name ) {
-			// Handle executable functions
-			if ( jQuery.isFunction( value ) )
-				value = value.call( elem, i );
-				
-			// Handle passing in a number to a CSS property
-			return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
-				value + "px" :
-				value;
-	},
-
-	className: {
-		// internal only, use addClass("class")
-		add: function( elem, classNames ) {
-			jQuery.each((classNames || "").split(/\s+/), function(i, className){
-				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
-					elem.className += (elem.className ? " " : "") + className;
-			});
-		},
-
-		// internal only, use removeClass("class")
-		remove: function( elem, classNames ) {
-			if (elem.nodeType == 1)
-				elem.className = classNames != undefined ?
-					jQuery.grep(elem.className.split(/\s+/), function(className){
-						return !jQuery.className.has( classNames, className );	
-					}).join(" ") :
-					"";
-		},
-
-		// internal only, use is(".class")
-		has: function( elem, className ) {
-			return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
-		}
-	},
-
-	// A method for quickly swapping in/out CSS properties to get correct calculations
-	swap: function( elem, options, callback ) {
-		var old = {};
-		// Remember the old values, and insert the new ones
-		for ( var name in options ) {
-			old[ name ] = elem.style[ name ];
-			elem.style[ name ] = options[ name ];
-		}
-
-		callback.call( elem );
-
-		// Revert the old values
-		for ( var name in options )
-			elem.style[ name ] = old[ name ];
-	},
-
-	css: function( elem, name, force ) {
-		if ( name == "width" || name == "height" ) {
-			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
-		
-			function getWH() {
-				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
-				var padding = 0, border = 0;
-				jQuery.each( which, function() {
-					padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
-					border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
-				});
-				val -= Math.round(padding + border);
-			}
-		
-			if ( jQuery(elem).is(":visible") )
-				getWH();
-			else
-				jQuery.swap( elem, props, getWH );
-			
-			return Math.max(0, val);
-		}
-		
-		return jQuery.curCSS( elem, name, force );
-	},
-
-	curCSS: function( elem, name, force ) {
-		var ret;
-
-		// A helper method for determining if an element's values are broken
-		function color( elem ) {
-			if ( !jQuery.browser.safari )
-				return false;
-
-			var ret = document.defaultView.getComputedStyle( elem, null );
-			return !ret || ret.getPropertyValue("color") == "";
-		}
-
-		// We need to handle opacity special in IE
-		if ( name == "opacity" && jQuery.browser.msie ) {
-			ret = jQuery.attr( elem.style, "opacity" );
-
-			return ret == "" ?
-				"1" :
-				ret;
-		}
-		// Opera sometimes will give the wrong display answer, this fixes it, see #2037
-		if ( jQuery.browser.opera && name == "display" ) {
-			var save = elem.style.outline;
-			elem.style.outline = "0 solid black";
-			elem.style.outline = save;
-		}
-		
-		// Make sure we're using the right name for getting the float value
-		if ( name.match( /float/i ) )
-			name = styleFloat;
-
-		if ( !force && elem.style && elem.style[ name ] )
-			ret = elem.style[ name ];
-
-		else if ( document.defaultView && document.defaultView.getComputedStyle ) {
-
-			// Only "float" is needed here
-			if ( name.match( /float/i ) )
-				name = "float";
-
-			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
-
-			var getComputedStyle = document.defaultView.getComputedStyle( elem, null );
-
-			if ( getComputedStyle && !color( elem ) )
-				ret = getComputedStyle.getPropertyValue( name );
-
-			// If the element isn't reporting its values properly in Safari
-			// then some display: none elements are involved
-			else {
-				var swap = [], stack = [];
-
-				// Locate all of the parent display: none elements
-				for ( var a = elem; a && color(a); a = a.parentNode )
-					stack.unshift(a);
-
-				// Go through and make them visible, but in reverse
-				// (It would be better if we knew the exact display type that they had)
-				for ( var i = 0; i < stack.length; i++ )
-					if ( color( stack[ i ] ) ) {
-						swap[ i ] = stack[ i ].style.display;
-						stack[ i ].style.display = "block";
-					}
-
-				// Since we flip the display style, we have to handle that
-				// one special, otherwise get the value
-				ret = name == "display" && swap[ stack.length - 1 ] != null ?
-					"none" :
-					( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || "";
-
-				// Finally, revert the display styles back
-				for ( var i = 0; i < swap.length; i++ )
-					if ( swap[ i ] != null )
-						stack[ i ].style.display = swap[ i ];
-			}
-
-			// We should always get a number back from opacity
-			if ( name == "opacity" && ret == "" )
-				ret = "1";
-
-		} else if ( elem.currentStyle ) {
-			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
-
-			// From the awesome hack by Dean Edwards
-			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
-
-			// If we're not dealing with a regular pixel number
-			// but a number that has a weird ending, we need to convert it to pixels
-			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
-				// Remember the original values
-				var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left;
-
-				// Put in the new values to get a computed value out
-				elem.runtimeStyle.left = elem.currentStyle.left;
-				elem.style.left = ret || 0;
-				ret = elem.style.pixelLeft + "px";
-
-				// Revert the changed values
-				elem.style.left = style;
-				elem.runtimeStyle.left = runtimeStyle;
-			}
-		}
-
-		return ret;
-	},
-	
-	clean: function( elems, context ) {
-		var ret = [];
-		context = context || document;
-		// !context.createElement fails in IE with an error but returns typeof 'object'
-		if (typeof context.createElement == 'undefined') 
-			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
-
-		jQuery.each(elems, function(i, elem){
-			if ( !elem )
-				return;
-
-			if ( elem.constructor == Number )
-				elem = elem.toString();
-			
-			// Convert html string into DOM nodes
-			if ( typeof elem == "string" ) {
-				// Fix "XHTML"-style tags in all browsers
-				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
-					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
-						all :
-						front + "></" + tag + ">";
-				});
-
-				// Trim whitespace, otherwise indexOf won't work as expected
-				var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
-
-				var wrap =
-					// option or optgroup
-					!tags.indexOf("<opt") &&
-					[ 1, "<select multiple='multiple'>", "</select>" ] ||
-					
-					!tags.indexOf("<leg") &&
-					[ 1, "<fieldset>", "</fieldset>" ] ||
-					
-					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
-					[ 1, "<table>", "</table>" ] ||
-					
-					!tags.indexOf("<tr") &&
-					[ 2, "<table><tbody>", "</tbody></table>" ] ||
-					
-				 	// <thead> matched above
-					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
-					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
-					
-					!tags.indexOf("<col") &&
-					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
-
-					// IE can't serialize <link> and <script> tags normally
-					jQuery.browser.msie &&
-					[ 1, "div<div>", "</div>" ] ||
-					
-					[ 0, "", "" ];
-
-				// Go to html and back, then peel off extra wrappers
-				div.innerHTML = wrap[1] + elem + wrap[2];
-				
-				// Move to the right depth
-				while ( wrap[0]-- )
-					div = div.lastChild;
-				
-				// Remove IE's autoinserted <tbody> from table fragments
-				if ( jQuery.browser.msie ) {
-					
-					// String was a <table>, *may* have spurious <tbody>
-					var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
-						div.firstChild && div.firstChild.childNodes :
-						
-						// String was a bare <thead> or <tfoot>
-						wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
-							div.childNodes :
-							[];
-				
-					for ( var j = tbody.length - 1; j >= 0 ; --j )
-						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
-							tbody[ j ].parentNode.removeChild( tbody[ j ] );
-					
-					// IE completely kills leading whitespace when innerHTML is used	
-					if ( /^\s/.test( elem ) )	
-						div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
-				
-				}
-				
-				elem = jQuery.makeArray( div.childNodes );
-			}
-
-			if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
-				return;
-
-			if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
-				ret.push( elem );
-
-			else
-				ret = jQuery.merge( ret, elem );
-
-		});
-
-		return ret;
-	},
-	
-	attr: function( elem, name, value ) {
-		// don't set attributes on text and comment nodes
-		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
-			return undefined;
-
-		var fix = jQuery.isXMLDoc( elem ) ?
-			{} :
-			jQuery.props;
-
-		// Safari mis-reports the default selected property of a hidden option
-		// Accessing the parent's selectedIndex property fixes it
-		if ( name == "selected" && jQuery.browser.safari )
-			elem.parentNode.selectedIndex;
-		
-		// Certain attributes only work when accessed via the old DOM 0 way
-		if ( fix[ name ] ) {
-			if ( value != undefined )
-				elem[ fix[ name ] ] = value;
-
-			return elem[ fix[ name ] ];
-
-		} else if ( jQuery.browser.msie && name == "style" )
-			return jQuery.attr( elem.style, "cssText", value );
-
-		else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName( elem, "form" ) && (name == "action" || name == "method") )
-			return elem.getAttributeNode( name ).nodeValue;
-
-		// IE elem.getAttribute passes even for style
-		else if ( elem.tagName ) {
-
-			if ( value != undefined ) {
-				// We can't allow the type property to be changed (since it causes problems in IE)
-				if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
-					throw "type property can't be changed";
-
-				// convert the value to a string (all browsers do this but IE) see #1070
-				elem.setAttribute( name, "" + value );
-			}
-
-			if ( jQuery.browser.msie && /href|src/.test( name ) && !jQuery.isXMLDoc( elem ) ) 
-				return elem.getAttribute( name, 2 );
-
-			return elem.getAttribute( name );
-
-		// elem is actually elem.style ... set the style
-		} else {
-			// IE actually uses filters for opacity
-			if ( name == "opacity" && jQuery.browser.msie ) {
-				if ( value != undefined ) {
-					// IE has trouble with opacity if it does not have layout
-					// Force it by setting the zoom level
-					elem.zoom = 1; 
-	
-					// Set the alpha filter to set the opacity
-					elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
-						(parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
-				}
-	
-				return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
-					(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() :
-					"";
-			}
-
-			name = name.replace(/-([a-z])/ig, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			if ( value != undefined )
-				elem[ name ] = value;
-
-			return elem[ name ];
-		}
-	},
-	
-	trim: function( text ) {
-		return (text || "").replace( /^\s+|\s+$/g, "" );
-	},
-
-	makeArray: function( array ) {
-		var ret = [];
-
-		// Need to use typeof to fight Safari childNodes crashes
-		if ( typeof array != "array" )
-			for ( var i = 0, length = array.length; i < length; i++ )
-				ret.push( array[ i ] );
-		else
-			ret = array.slice( 0 );
-
-		return ret;
-	},
-
-	inArray: function( elem, array ) {
-		for ( var i = 0, length = array.length; i < length; i++ )
-			if ( array[ i ] == elem )
-				return i;
-
-		return -1;
-	},
-
-	merge: function( first, second ) {
-		// We have to loop this way because IE & Opera overwrite the length
-		// expando of getElementsByTagName
-
-		// Also, we need to make sure that the correct elements are being returned
-		// (IE returns comment nodes in a '*' query)
-		if ( jQuery.browser.msie ) {
-			for ( var i = 0; second[ i ]; i++ )
-				if ( second[ i ].nodeType != 8 )
-					first.push( second[ i ] );
-
-		} else
-			for ( var i = 0; second[ i ]; i++ )
-				first.push( second[ i ] );
-
-		return first;
-	},
-
-	unique: function( array ) {
-		var ret = [], done = {};
-
-		try {
-
-			for ( var i = 0, length = array.length; i < length; i++ ) {
-				var id = jQuery.data( array[ i ] );
-
-				if ( !done[ id ] ) {
-					done[ id ] = true;
-					ret.push( array[ i ] );
-				}
-			}
-
-		} catch( e ) {
-			ret = array;
-		}
-
-		return ret;
-	},
-
-	grep: function( elems, callback, inv ) {
-		var ret = [];
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( var i = 0, length = elems.length; i < length; i++ )
-			if ( !inv && callback( elems[ i ], i ) || inv && !callback( elems[ i ], i ) )
-				ret.push( elems[ i ] );
-
-		return ret;
-	},
-
-	map: function( elems, callback ) {
-		var ret = [];
-
-		// Go through the array, translating each of the items to their
-		// new value (or values).
-		for ( var i = 0, length = elems.length; i < length; i++ ) {
-			var value = callback( elems[ i ], i );
-
-			if ( value !== null && value != undefined ) {
-				if ( value.constructor != Array )
-					value = [ value ];
-
-				ret = ret.concat( value );
-			}
-		}
-
-		return ret;
-	}
-});
-
-var userAgent = navigator.userAgent.toLowerCase();
-
-// Figure out what browser is being used
-jQuery.browser = {
-	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
-	safari: /webkit/.test( userAgent ),
-	opera: /opera/.test( userAgent ),
-	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
-	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
-};
-
-var styleFloat = jQuery.browser.msie ?
-	"styleFloat" :
-	"cssFloat";
-	
-jQuery.extend({
-	// Check to see if the W3C box model is being used
-	boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
-	
-	props: {
-		"for": "htmlFor",
-		"class": "className",
-		"float": styleFloat,
-		cssFloat: styleFloat,
-		styleFloat: styleFloat,
-		innerHTML: "innerHTML",
-		className: "className",
-		value: "value",
-		disabled: "disabled",
-		checked: "checked",
-		readonly: "readOnly",
-		selected: "selected",
-		maxlength: "maxLength",
-		selectedIndex: "selectedIndex",
-		defaultValue: "defaultValue",
-		tagName: "tagName",
-		nodeName: "nodeName"
-	}
-});
-
-jQuery.each({
-	parent: function(elem){return elem.parentNode;},
-	parents: function(elem){return jQuery.dir(elem,"parentNode");},
-	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
-	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
-	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
-	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
-	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
-	children: function(elem){return jQuery.sibling(elem.firstChild);},
-	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
-}, function(name, fn){
-	jQuery.fn[ name ] = function( selector ) {
-		var ret = jQuery.map( this, fn );
-
-		if ( selector && typeof selector == "string" )
-			ret = jQuery.multiFilter( selector, ret );
-
-		return this.pushStack( jQuery.unique( ret ) );
-	};
-});
-
-jQuery.each({
-	appendTo: "append",
-	prependTo: "prepend",
-	insertBefore: "before",
-	insertAfter: "after",
-	replaceAll: "replaceWith"
-}, function(name, original){
-	jQuery.fn[ name ] = function() {
-		var args = arguments;
-
-		return this.each(function(){
-			for ( var i = 0, length = args.length; i < length; i++ )
-				jQuery( args[ i ] )[ original ]( this );
-		});
-	};
-});
-
-jQuery.each({
-	removeAttr: function( name ) {
-		jQuery.attr( this, name, "" );
-		if (this.nodeType == 1) 
-			this.removeAttribute( name );
-	},
-
-	addClass: function( classNames ) {
-		jQuery.className.add( this, classNames );
-	},
-
-	removeClass: function( classNames ) {
-		jQuery.className.remove( this, classNames );
-	},
-
-	toggleClass: function( classNames ) {
-		jQuery.className[ jQuery.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
-	},
-
-	remove: function( selector ) {
-		if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
-			// Prevent memory leaks
-			jQuery( "*", this ).add(this).each(function(){
-				jQuery.event.remove(this);
-				jQuery.removeData(this);
-			});
-			if (this.parentNode)
-				this.parentNode.removeChild( this );
-		}
-	},
-
-	empty: function() {
-		// Remove element nodes and prevent memory leaks
-		jQuery( ">*", this ).remove();
-		
-		// Remove any remaining nodes
-		while ( this.firstChild )
-			this.removeChild( this.firstChild );
-	}
-}, function(name, fn){
-	jQuery.fn[ name ] = function(){
-		return this.each( fn, arguments );
-	};
-});
-
-jQuery.each([ "Height", "Width" ], function(i, name){
-	var type = name.toLowerCase();
-	
-	jQuery.fn[ type ] = function( size ) {
-		// Get window width or height
-		return this[0] == window ?
-			// Opera reports document.body.client[Width/Height] properly in both quirks and standards
-			jQuery.browser.opera && document.body[ "client" + name ] || 
-			
-			// Safari reports inner[Width/Height] just fine (Mozilla and Opera include scroll bar widths)
-			jQuery.browser.safari && window[ "inner" + name ] ||
-			
-			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
-			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || document.body[ "client" + name ] :
-		
-			// Get document width or height
-			this[0] == document ?
-				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
-				Math.max( 
-					Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), 
-					Math.max(document.body["offset" + name], document.documentElement["offset" + name]) 
-				) :
-
-				// Get or set width or height on the element
-				size == undefined ?
-					// Get width or height on the element
-					(this.length ? jQuery.css( this[0], type ) : null) :
-
-					// Set the width or height on the element (default to pixels if value is unitless)
-					this.css( type, size.constructor == String ? size : size + "px" );
-	};
-});
-
-var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417 ?
-		"(?:[\\w*_-]|\\\\.)" :
-		"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
-	quickChild = new RegExp("^>\\s*(" + chars + "+)"),
-	quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),
-	quickClass = new RegExp("^([#.]?)(" + chars + "*)");
-
-jQuery.extend({
-	expr: {
-		"": function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},
-		"#": function(a,i,m){return a.getAttribute("id")==m[2];},
-		":": {
-			// Position Checks
-			lt: function(a,i,m){return i<m[3]-0;},
-			gt: function(a,i,m){return i>m[3]-0;},
-			nth: function(a,i,m){return m[3]-0==i;},
-			eq: function(a,i,m){return m[3]-0==i;},
-			first: function(a,i){return i==0;},
-			last: function(a,i,m,r){return i==r.length-1;},
-			even: function(a,i){return i%2==0;},
-			odd: function(a,i){return i%2;},
-
-			// Child Checks
-			"first-child": function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},
-			"last-child": function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},
-			"only-child": function(a){return !jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},
-
-			// Parent Checks
-			parent: function(a){return a.firstChild;},
-			empty: function(a){return !a.firstChild;},
-
-			// Text Check
-			contains: function(a,i,m){return (a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},
-
-			// Visibility
-			visible: function(a){return "hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},
-			hidden: function(a){return "hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},
-
-			// Form attributes
-			enabled: function(a){return !a.disabled;},
-			disabled: function(a){return a.disabled;},
-			checked: function(a){return a.checked;},
-			selected: function(a){return a.selected||jQuery.attr(a,"selected");},
-
-			// Form elements
-			text: function(a){return "text"==a.type;},
-			radio: function(a){return "radio"==a.type;},
-			checkbox: function(a){return "checkbox"==a.type;},
-			file: function(a){return "file"==a.type;},
-			password: function(a){return "password"==a.type;},
-			submit: function(a){return "submit"==a.type;},
-			image: function(a){return "image"==a.type;},
-			reset: function(a){return "reset"==a.type;},
-			button: function(a){return "button"==a.type||jQuery.nodeName(a,"button");},
-			input: function(a){return /input|select|textarea|button/i.test(a.nodeName);},
-
-			// :has()
-			has: function(a,i,m){return jQuery.find(m[3],a).length;},
-
-			// :header
-			header: function(a){return /h\d/i.test(a.nodeName);},
-
-			// :animated
-			animated: function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}
-		}
-	},
-	
-	// The regular expressions that power the parsing engine
-	parse: [
-		// Match: [@value='test'], [@foo]
-		/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
-
-		// Match: :contains('foo')
-		/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
-
-		// Match: :even, :last-chlid, #id, .class
-		new RegExp("^([:.#]*)(" + chars + "+)")
-	],
-
-	multiFilter: function( expr, elems, not ) {
-		var old, cur = [];
-
-		while ( expr && expr != old ) {
-			old = expr;
-			var f = jQuery.filter( expr, elems, not );
-			expr = f.t.replace(/^\s*,\s*/, "" );
-			cur = not ? elems = f.r : jQuery.merge( cur, f.r );
-		}
-
-		return cur;
-	},
-
-	find: function( t, context ) {
-		// Quickly handle non-string expressions
-		if ( typeof t != "string" )
-			return [ t ];
-
-		// check to make sure context is a DOM element or a document
-		if ( context && context.nodeType != 1 && context.nodeType != 9)
-			return [ ];
-
-		// Set the correct context (if none is provided)
-		context = context || document;
-
-		// Initialize the search
-		var ret = [context], done = [], last, nodeName;
-
-		// Continue while a selector expression exists, and while
-		// we're no longer looping upon ourselves
-		while ( t && last != t ) {
-			var r = [];
-			last = t;
-
-			t = jQuery.trim(t);
-
-			var foundToken = false;
-
-			// An attempt at speeding up child selectors that
-			// point to a specific element tag
-			var re = quickChild;
-			var m = re.exec(t);
-
-			if ( m ) {
-				nodeName = m[1].toUpperCase();
-
-				// Perform our own iteration and filter
-				for ( var i = 0; ret[i]; i++ )
-					for ( var c = ret[i].firstChild; c; c = c.nextSibling )
-						if ( c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName) )
-							r.push( c );
-
-				ret = r;
-				t = t.replace( re, "" );
-				if ( t.indexOf(" ") == 0 ) continue;
-				foundToken = true;
-			} else {
-				re = /^([>+~])\s*(\w*)/i;
-
-				if ( (m = re.exec(t)) != null ) {
-					r = [];
-
-					var merge = {};
-					nodeName = m[2].toUpperCase();
-					m = m[1];
-
-					for ( var j = 0, rl = ret.length; j < rl; j++ ) {
-						var n = m == "~" || m == "+" ? ret[j].nextSibling : ret[j].firstChild;
-						for ( ; n; n = n.nextSibling )
-							if ( n.nodeType == 1 ) {
-								var id = jQuery.data(n);
-
-								if ( m == "~" && merge[id] ) break;
-								
-								if (!nodeName || n.nodeName.toUpperCase() == nodeName ) {
-									if ( m == "~" ) merge[id] = true;
-									r.push( n );
-								}
-								
-								if ( m == "+" ) break;
-							}
-					}
-
-					ret = r;
-
-					// And remove the token
-					t = jQuery.trim( t.replace( re, "" ) );
-					foundToken = true;
-				}
-			}
-
-			// See if there's still an expression, and that we haven't already
-			// matched a token
-			if ( t && !foundToken ) {
-				// Handle multiple expressions
-				if ( !t.indexOf(",") ) {
-					// Clean the result set
-					if ( context == ret[0] ) ret.shift();
-
-					// Merge the result sets
-					done = jQuery.merge( done, ret );
-
-					// Reset the context
-					r = ret = [context];
-
-					// Touch up the selector string
-					t = " " + t.substr(1,t.length);
-
-				} else {
-					// Optimize for the case nodeName#idName
-					var re2 = quickID;
-					var m = re2.exec(t);
-					
-					// Re-organize the results, so that they're consistent
-					if ( m ) {
-						m = [ 0, m[2], m[3], m[1] ];
-
-					} else {
-						// Otherwise, do a traditional filter check for
-						// ID, class, and element selectors
-						re2 = quickClass;
-						m = re2.exec(t);
-					}
-
-					m[2] = m[2].replace(/\\/g, "");
-
-					var elem = ret[ret.length-1];
-
-					// Try to do a global search by ID, where we can
-					if ( m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem) ) {
-						// Optimization for HTML document case
-						var oid = elem.getElementById(m[2]);
-						
-						// Do a quick check for the existence of the actual ID attribute
-						// to avoid selecting by the name attribute in IE
-						// also check to insure id is a string to avoid selecting an element with the name of 'id' inside a form
-						if ( (jQuery.browser.msie||jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2] )
-							oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
-
-						// Do a quick check for node name (where applicable) so
-						// that div#foo searches will be really fast
-						ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
-					} else {
-						// We need to find all descendant elements
-						for ( var i = 0; ret[i]; i++ ) {
-							// Grab the tag name being searched for
-							var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*" : m[2];
-
-							// Handle IE7 being really dumb about <object>s
-							if ( tag == "*" && ret[i].nodeName.toLowerCase() == "object" )
-								tag = "param";
-
-							r = jQuery.merge( r, ret[i].getElementsByTagName( tag ));
-						}
-
-						// It's faster to filter by class and be done with it
-						if ( m[1] == "." )
-							r = jQuery.classFilter( r, m[2] );
-
-						// Same with ID filtering
-						if ( m[1] == "#" ) {
-							var tmp = [];
-
-							// Try to find the element with the ID
-							for ( var i = 0; r[i]; i++ )
-								if ( r[i].getAttribute("id") == m[2] ) {
-									tmp = [ r[i] ];
-									break;
-								}
-
-							r = tmp;
-						}
-
-						ret = r;
-					}
-
-					t = t.replace( re2, "" );
-				}
-
-			}
-
-			// If a selector string still exists
-			if ( t ) {
-				// Attempt to filter it
-				var val = jQuery.filter(t,r);
-				ret = r = val.r;
-				t = jQuery.trim(val.t);
-			}
-		}
-
-		// An error occurred with the selector;
-		// just return an empty set instead
-		if ( t )
-			ret = [];
-
-		// Remove the root context
-		if ( ret && context == ret[0] )
-			ret.shift();
-
-		// And combine the results
-		done = jQuery.merge( done, ret );
-
-		return done;
-	},
-
-	classFilter: function(r,m,not){
-		m = " " + m + " ";
-		var tmp = [];
-		for ( var i = 0; r[i]; i++ ) {
-			var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
-			if ( !not && pass || not && !pass )
-				tmp.push( r[i] );
-		}
-		return tmp;
-	},
-
-	filter: function(t,r,not) {
-		var last;
-
-		// Look for common filter expressions
-		while ( t && t != last ) {
-			last = t;
-
-			var p = jQuery.parse, m;
-
-			for ( var i = 0; p[i]; i++ ) {
-				m = p[i].exec( t );
-
-				if ( m ) {
-					// Remove what we just matched
-					t = t.substring( m[0].length );
-
-					m[2] = m[2].replace(/\\/g, "");
-					break;
-				}
-			}
-
-			if ( !m )
-				break;
-
-			// :not() is a special case that can be optimized by
-			// keeping it out of the expression list
-			if ( m[1] == ":" && m[2] == "not" )
-				// optimize if only one selector found (most common case)
-				r = isSimple.test( m[3] ) ?
-					jQuery.filter(m[3], r, true).r :
-					jQuery( r ).not( m[3] );
-
-			// We can get a big speed boost by filtering by class here
-			else if ( m[1] == "." )
-				r = jQuery.classFilter(r, m[2], not);
-
-			else if ( m[1] == "[" ) {
-				var tmp = [], type = m[3];
-				
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var a = r[i], z = a[ jQuery.props[m[2]] || m[2] ];
-					
-					if ( z == null || /href|src|selected/.test(m[2]) )
-						z = jQuery.attr(a,m[2]) || '';
-
-					if ( (type == "" && !!z ||
-						 type == "=" && z == m[5] ||
-						 type == "!=" && z != m[5] ||
-						 type == "^=" && z && !z.indexOf(m[5]) ||
-						 type == "$=" && z.substr(z.length - m[5].length) == m[5] ||
-						 (type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0) ^ not )
-							tmp.push( a );
-				}
-				
-				r = tmp;
-
-			// We can get a speed boost by handling nth-child here
-			} else if ( m[1] == ":" && m[2] == "nth-child" ) {
-				var merge = {}, tmp = [],
-					// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
-					test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
-						m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||
-						!/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
-					// calculate the numbers (first)n+(last) including if they are negative
-					first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
- 
-				// loop through all the elements left in the jQuery object
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
-
-					if ( !merge[id] ) {
-						var c = 1;
-
-						for ( var n = parentNode.firstChild; n; n = n.nextSibling )
-							if ( n.nodeType == 1 )
-								n.nodeIndex = c++;
-
-						merge[id] = true;
-					}
-
-					var add = false;
-
-					if ( first == 0 ) {
-						if ( node.nodeIndex == last )
-							add = true;
-					} else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
-						add = true;
-
-					if ( add ^ not )
-						tmp.push( node );
-				}
-
-				r = tmp;
-
-			// Otherwise, find the expression to execute
-			} else {
-				var fn = jQuery.expr[ m[1] ];
-				if ( typeof fn == "object" )
-					fn = fn[ m[2] ];
-
-				if ( typeof fn == "string" )
-					fn = eval("false||function(a,i){return " + fn + ";}");
-
-				// Execute it against the current filter
-				r = jQuery.grep( r, function(elem, i){
-					return fn(elem, i, m, r);
-				}, not );
-			}
-		}
-
-		// Return an array of filtered elements (r)
-		// and the modified expression string (t)
-		return { r: r, t: t };
-	},
-
-	dir: function( elem, dir ){
-		var matched = [];
-		var cur = elem[dir];
-		while ( cur && cur != document ) {
-			if ( cur.nodeType == 1 )
-				matched.push( cur );
-			cur = cur[dir];
-		}
-		return matched;
-	},
-	
-	nth: function(cur,result,dir,elem){
-		result = result || 1;
-		var num = 0;
-
-		for ( ; cur; cur = cur[dir] )
-			if ( cur.nodeType == 1 && ++num == result )
-				break;
-
-		return cur;
-	},
-	
-	sibling: function( n, elem ) {
-		var r = [];
-
-		for ( ; n; n = n.nextSibling ) {
-			if ( n.nodeType == 1 && (!elem || n != elem) )
-				r.push( n );
-		}
-
-		return r;
-	}
-});
-
-/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code orignated from 
- * Dean Edwards' addEvent library.
- */
-jQuery.event = {
-
-	// Bind an event to an element
-	// Original by Dean Edwards
-	add: function(elem, types, handler, data) {
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		// For whatever reason, IE has trouble passing the window object
-		// around, causing it to be cloned in the process
-		if ( jQuery.browser.msie && elem.setInterval != undefined )
-			elem = window;
-
-		// Make sure that the function being executed has a unique ID
-		if ( !handler.guid )
-			handler.guid = this.guid++;
-			
-		// if data is passed, bind to handler 
-		if( data != undefined ) { 
-			// Create temporary function pointer to original handler 
-			var fn = handler; 
-
-			// Create unique handler function, wrapped around original handler 
-			handler = function() { 
-				// Pass arguments and context to original handler 
-				return fn.apply(this, arguments); 
-			};
-
-			// Store data in unique handler 
-			handler.data = data;
-
-			// Set the guid of unique handler to the same of original handler, so it can be removed 
-			handler.guid = fn.guid;
-		}
-
-		// Init the element's event structure
-		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
-			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
-				// returned undefined or false
-				var val;
-
-				// Handle the second event of a trigger and when
-				// an event is called after a page has unloaded
-				if ( typeof jQuery == "undefined" || jQuery.event.triggered )
-					return val;
-		
-				val = jQuery.event.handle.apply(arguments.callee.elem, arguments);
-		
-				return val;
-			});
-		// Add elem as a property of the handle function
-		// This is to prevent a memory leak with non-native
-		// event in IE.
-		handle.elem = elem;
-			
-			// Handle multiple events seperated by a space
-			// jQuery(...).bind("mouseover mouseout", fn);
-			jQuery.each(types.split(/\s+/), function(index, type) {
-				// Namespaced event handlers
-				var parts = type.split(".");
-				type = parts[0];
-				handler.type = parts[1];
-
-				// Get the current list of functions bound to this event
-				var handlers = events[type];
-
-				// Init the event handler queue
-				if (!handlers) {
-					handlers = events[type] = {};
-		
-					// Check for a special event handler
-					// Only use addEventListener/attachEvent if the special
-					// events handler returns false
-					if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) {
-						// Bind the global event handler to the element
-						if (elem.addEventListener)
-							elem.addEventListener(type, handle, false);
-						else if (elem.attachEvent)
-							elem.attachEvent("on" + type, handle);
-					}
-				}
-
-				// Add the function to the element's handler list
-				handlers[handler.guid] = handler;
-
-				// Keep track of which events have been used, for global triggering
-				jQuery.event.global[type] = true;
-			});
-		
-		// Nullify elem to prevent memory leaks in IE
-		elem = null;
-	},
-
-	guid: 1,
-	global: {},
-
-	// Detach an event or set of events from an element
-	remove: function(elem, types, handler) {
-		// don't do events on text and comment nodes
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		var events = jQuery.data(elem, "events"), ret, index;
-
-		if ( events ) {
-			// Unbind all events for the element
-			if ( types == undefined || (typeof types == "string" && types.charAt(0) == ".") )
-				for ( var type in events )
-					this.remove( elem, type + (types || "") );
-			else {
-				// types is actually an event object here
-				if ( types.type ) {
-					handler = types.handler;
-					types = types.type;
-				}
-				
-				// Handle multiple events seperated by a space
-				// jQuery(...).unbind("mouseover mouseout", fn);
-				jQuery.each(types.split(/\s+/), function(index, type){
-					// Namespaced event handlers
-					var parts = type.split(".");
-					type = parts[0];
-					
-					if ( events[type] ) {
-						// remove the given handler for the given type
-						if ( handler )
-							delete events[type][handler.guid];
-			
-						// remove all handlers for the given type
-						else
-							for ( handler in events[type] )
-								// Handle the removal of namespaced events
-								if ( !parts[1] || events[type][handler].type == parts[1] )
-									delete events[type][handler];
-
-						// remove generic event handler if no more handlers exist
-						for ( ret in events[type] ) break;
-						if ( !ret ) {
-							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) {
-								if (elem.removeEventListener)
-									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
-								else if (elem.detachEvent)
-									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
-							}
-							ret = null;
-							delete events[type];
-						}
-					}
-				});
-			}
-
-			// Remove the expando if it's no longer used
-			for ( ret in events ) break;
-			if ( !ret ) {
-				var handle = jQuery.data( elem, "handle" );
-				if ( handle ) handle.elem = null;
-				jQuery.removeData( elem, "events" );
-				jQuery.removeData( elem, "handle" );
-			}
-		}
-	},
-
-	trigger: function(type, data, elem, donative, extra) {
-		// Clone the incoming data, if any
-		data = jQuery.makeArray(data || []);
-
-		if ( type.indexOf("!") >= 0 ) {
-			type = type.slice(0, -1);
-			var exclusive = true;
-		}
-
-		// Handle a global trigger
-		if ( !elem ) {
-			// Only trigger if we've ever bound an event for it
-			if ( this.global[type] )
-				jQuery("*").add([window, document]).trigger(type, data);
-
-		// Handle triggering a single element
-		} else {
-			// don't do events on text and comment nodes
-			if ( elem.nodeType == 3 || elem.nodeType == 8 )
-				return undefined;
-
-			var val, ret, fn = jQuery.isFunction( elem[ type ] || null ),
-				// Check to see if we need to provide a fake event, or not
-				event = !data[0] || !data[0].preventDefault;
-			
-			// Pass along a fake event
-			if ( event )
-				data.unshift( this.fix({ type: type, target: elem }) );
-
-			// Enforce the right trigger type
-			data[0].type = type;
-			if ( exclusive )
-				data[0].exclusive = true;
-
-			// Trigger the event
-			if ( jQuery.isFunction( jQuery.data(elem, "handle") ) )
-				val = jQuery.data(elem, "handle").apply( elem, data );
-
-			// Handle triggering native .onfoo handlers
-			if ( !fn && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
-				val = false;
-
-			// Extra functions don't get the custom event object
-			if ( event )
-				data.shift();
-
-			// Handle triggering of extra function
-			if ( extra && jQuery.isFunction( extra ) ) {
-				// call the extra function and tack the current return value on the end for possible inspection
-				ret = extra.apply( elem, val == null ? data : data.concat( val ) );
-				// if anything is returned, give it precedence and have it overwrite the previous value
-				if (ret !== undefined)
-					val = ret;
-			}
-
-			// Trigger the native events (except for clicks on links)
-			if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
-				this.triggered = true;
-				try {
-					elem[ type ]();
-				// prevent IE from throwing an error for some hidden elements
-				} catch (e) {}
-			}
-
-			this.triggered = false;
-		}
-
-		return val;
-	},
-
-	handle: function(event) {
-		// returned undefined or false
-		var val;
-
-		// Empty object is for triggered events with no data
-		event = jQuery.event.fix( event || window.event || {} ); 
-
-		// Namespaced event handlers
-		var parts = event.type.split(".");
-		event.type = parts[0];
-
-		var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
-		args.unshift( event );
-
-		for ( var j in handlers ) {
-			var handler = handlers[j];
-			// Pass in a reference to the handler function itself
-			// So that we can later remove it
-			args[0].handler = handler;
-			args[0].data = handler.data;
-
-			// Filter the functions by class
-			if ( !parts[1] && !event.exclusive || handler.type == parts[1] ) {
-				var ret = handler.apply( this, args );
-
-				if ( val !== false )
-					val = ret;
-
-				if ( ret === false ) {
-					event.preventDefault();
-					event.stopPropagation();
-				}
-			}
-		}
-
-		// Clean up added properties in IE to prevent memory leak
-		if (jQuery.browser.msie)
-			event.target = event.preventDefault = event.stopPropagation =
-				event.handler = event.data = null;
-
-		return val;
-	},
-
-	fix: function(event) {
-		// store a copy of the original event object 
-		// and clone to set read-only properties
-		var originalEvent = event;
-		event = jQuery.extend({}, originalEvent);
-		
-		// add preventDefault and stopPropagation since 
-		// they will not work on the clone
-		event.preventDefault = function() {
-			// if preventDefault exists run it on the original event
-			if (originalEvent.preventDefault)
-				originalEvent.preventDefault();
-			// otherwise set the returnValue property of the original event to false (IE)
-			originalEvent.returnValue = false;
-		};
-		event.stopPropagation = function() {
-			// if stopPropagation exists run it on the original event
-			if (originalEvent.stopPropagation)
-				originalEvent.stopPropagation();
-			// otherwise set the cancelBubble property of the original event to true (IE)
-			originalEvent.cancelBubble = true;
-		};
-		
-		// Fix target property, if necessary
-		if ( !event.target )
-			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
-				
-		// check if target is a textnode (safari)
-		if ( event.target.nodeType == 3 )
-			event.target = originalEvent.target.parentNode;
-
-		// Add relatedTarget, if necessary
-		if ( !event.relatedTarget && event.fromElement )
-			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
-
-		// Calculate pageX/Y if missing and clientX/Y available
-		if ( event.pageX == null && event.clientX != null ) {
-			var doc = document.documentElement, body = document.body;
-			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
-			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
-		}
-			
-		// Add which for key events
-		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
-			event.which = event.charCode || event.keyCode;
-		
-		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
-		if ( !event.metaKey && event.ctrlKey )
-			event.metaKey = event.ctrlKey;
-
-		// Add which for click: 1 == left; 2 == middle; 3 == right
-		// Note: button is not normalized, so don't use it
-		if ( !event.which && event.button )
-			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
-			
-		return event;
-	},
-	
-	special: {
-		ready: {
-			setup: function() {
-				// Make sure the ready event is setup
-				bindReady();
-				return;
-			},
-			
-			teardown: function() { return; }
-		},
-		
-		mouseenter: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseenter
-				arguments[0].type = "mouseenter";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		},
-	
-		mouseleave: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseleave
-				arguments[0].type = "mouseleave";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		}
-	}
-};
-
-jQuery.fn.extend({
-	bind: function( type, data, fn ) {
-		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
-			jQuery.event.add( this, type, fn || data, fn && data );
-		});
-	},
-	
-	one: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.add( this, type, function(event) {
-				jQuery(this).unbind(event);
-				return (fn || data).apply( this, arguments);
-			}, fn && data);
-		});
-	},
-
-	unbind: function( type, fn ) {
-		return this.each(function(){
-			jQuery.event.remove( this, type, fn );
-		});
-	},
-
-	trigger: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.trigger( type, data, this, true, fn );
-		});
-	},
-
-	triggerHandler: function( type, data, fn ) {
-		if ( this[0] )
-			return jQuery.event.trigger( type, data, this[0], false, fn );
-		return undefined;
-	},
-
-	toggle: function() {
-		// Save reference to arguments for access in closure
-		var args = arguments;
-
-		return this.click(function(event) {
-			// Figure out which function to execute
-			this.lastToggle = 0 == this.lastToggle ? 1 : 0;
-			
-			// Make sure that clicks stop
-			event.preventDefault();
-			
-			// and execute the function
-			return args[this.lastToggle].apply( this, arguments ) || false;
-		});
-	},
-
-	hover: function(fnOver, fnOut) {
-		return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
-	},
-	
-	ready: function(fn) {
-		// Attach the listeners
-		bindReady();
-
-		// If the DOM is already ready
-		if ( jQuery.isReady )
-			// Execute the function immediately
-			fn.call( document, jQuery );
-			
-		// Otherwise, remember the function for later
-		else
-			// Add the function to the wait list
-			jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
-	
-		return this;
-	}
-});
-
-jQuery.extend({
-	isReady: false,
-	readyList: [],
-	// Handle when the DOM is ready
-	ready: function() {
-		// Make sure that the DOM is not already loaded
-		if ( !jQuery.isReady ) {
-			// Remember that the DOM is ready
-			jQuery.isReady = true;
-			
-			// If there are functions bound, to execute
-			if ( jQuery.readyList ) {
-				// Execute all of them
-				jQuery.each( jQuery.readyList, function(){
-					this.apply( document );
-				});
-				
-				// Reset the list of functions
-				jQuery.readyList = null;
-			}
-		
-			// Trigger any bound ready events
-			jQuery(document).triggerHandler("ready");
-		}
-	}
-});
-
-var readyBound = false;
-
-function bindReady(){
-	if ( readyBound ) return;
-	readyBound = true;
-
-	// Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
-	if ( document.addEventListener && !jQuery.browser.opera)
-		// Use the handy event callback
-		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
-	
-	// If IE is used and is not in a frame
-	// Continually check to see if the document is ready
-	if ( jQuery.browser.msie && window == top ) (function(){
-		if (jQuery.isReady) return;
-		try {
-			// If IE is used, use the trick by Diego Perini
-			// http://javascript.nwbox.com/IEContentLoaded/
-			document.documentElement.doScroll("left");
-		} catch( error ) {
-			setTimeout( arguments.callee, 0 );
-			return;
-		}
-		// and execute any waiting functions
-		jQuery.ready();
-	})();
-
-	if ( jQuery.browser.opera )
-		document.addEventListener( "DOMContentLoaded", function () {
-			if (jQuery.isReady) return;
-			for (var i = 0; i < document.styleSheets.length; i++)
-				if (document.styleSheets[i].disabled) {
-					setTimeout( arguments.callee, 0 );
-					return;
-				}
-			// and execute any waiting functions
-			jQuery.ready();
-		}, false);
-
-	if ( jQuery.browser.safari ) {
-		var numStyles;
-		(function(){
-			if (jQuery.isReady) return;
-			if ( document.readyState != "loaded" && document.readyState != "complete" ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			if ( numStyles === undefined )
-				numStyles = jQuery("style, link[rel=stylesheet]").length;
-			if ( document.styleSheets.length != numStyles ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			// and execute any waiting functions
-			jQuery.ready();
-		})();
-	}
-
-	// A fallback to window.onload, that will always work
-	jQuery.event.add( window, "load", jQuery.ready );
-}
-
-jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
-	"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
-	"submit,keydown,keypress,keyup,error").split(","), function(i, name){
-	
-	// Handle event binding
-	jQuery.fn[name] = function(fn){
-		return fn ? this.bind(name, fn) : this.trigger(name);
-	};
-});
-
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function(event, elem) {
-	// Check if mouse(over|out) are still within the same parent element
-	var parent = event.relatedTarget;
-	// Traverse up the tree
-	while ( parent && parent != elem ) try { parent = parent.parentNode; } catch(error) { parent = elem; }
-	// Return true if we actually just moused on to a sub-element
-	return parent == elem;
-};
-
-// Prevent memory leaks in IE
-// And prevent errors on refresh with events like mouseover in other browsers
-// Window isn't included so as not to unbind existing unload events
-jQuery(window).bind("unload", function() {
-	jQuery("*").add(document).unbind();
-});
-jQuery.fn.extend({
-	load: function( url, params, callback ) {
-		if ( jQuery.isFunction( url ) )
-			return this.bind("load", url);
-
-		var off = url.indexOf(" ");
-		if ( off >= 0 ) {
-			var selector = url.slice(off, url.length);
-			url = url.slice(0, off);
-		}
-
-		callback = callback || function(){};
-
-		// Default to a GET request
-		var type = "GET";
-
-		// If the second parameter was provided
-		if ( params )
-			// If it's a function
-			if ( jQuery.isFunction( params ) ) {
-				// We assume that it's the callback
-				callback = params;
-				params = null;
-
-			// Otherwise, build a param string
-			} else {
-				params = jQuery.param( params );
-				type = "POST";
-			}
-
-		var self = this;
-
-		// Request the remote document
-		jQuery.ajax({
-			url: url,
-			type: type,
-			dataType: "html",
-			data: params,
-			complete: function(res, status){
-				// If successful, inject the HTML into all the matched elements
-				if ( status == "success" || status == "notmodified" )
-					// See if a selector was specified
-					self.html( selector ?
-						// Create a dummy div to hold the results
-						jQuery("<div/>")
-							// inject the contents of the document in, removing the scripts
-							// to avoid any 'Permission Denied' errors in IE
-							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
-
-							// Locate the specified elements
-							.find(selector) :
-
-						// If not, just inject the full result
-						res.responseText );
-
-				self.each( callback, [res.responseText, status, res] );
-			}
-		});
-		return this;
-	},
-
-	serialize: function() {
-		return jQuery.param(this.serializeArray());
-	},
-	serializeArray: function() {
-		return this.map(function(){
-			return jQuery.nodeName(this, "form") ?
-				jQuery.makeArray(this.elements) : this;
-		})
-		.filter(function(){
-			return this.name && !this.disabled && 
-				(this.checked || /select|textarea/i.test(this.nodeName) || 
-					/text|hidden|password/i.test(this.type));
-		})
-		.map(function(i, elem){
-			var val = jQuery(this).val();
-			return val == null ? null :
-				val.constructor == Array ?
-					jQuery.map( val, function(val, i){
-						return {name: elem.name, value: val};
-					}) :
-					{name: elem.name, value: val};
-		}).get();
-	}
-});
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
-	jQuery.fn[o] = function(f){
-		return this.bind(o, f);
-	};
-});
-
-var jsc = (new Date).getTime();
-
-jQuery.extend({
-	get: function( url, data, callback, type ) {
-		// shift arguments if data argument was ommited
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = null;
-		}
-		
-		return jQuery.ajax({
-			type: "GET",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	getScript: function( url, callback ) {
-		return jQuery.get(url, null, callback, "script");
-	},
-
-	getJSON: function( url, data, callback ) {
-		return jQuery.get(url, data, callback, "json");
-	},
-
-	post: function( url, data, callback, type ) {
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = {};
-		}
-
-		return jQuery.ajax({
-			type: "POST",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	ajaxSetup: function( settings ) {
-		jQuery.extend( jQuery.ajaxSettings, settings );
-	},
-
-	ajaxSettings: {
-		global: true,
-		type: "GET",
-		timeout: 0,
-		contentType: "application/x-www-form-urlencoded",
-		processData: true,
-		async: true,
-		data: null,
-		username: null,
-		password: null,
-		accepts: {
-			xml: "application/xml, text/xml",
-			html: "text/html",
-			script: "text/javascript, application/javascript",
-			json: "application/json, text/javascript",
-			text: "text/plain",
-			_default: "*/*"
-		}
-	},
-	
-	// Last-Modified header cache for next request
-	lastModified: {},
-
-	ajax: function( s ) {
-		var jsonp, jsre = /=\?(&|$)/g, status, data;
-
-		// Extend the settings, but re-extend 's' so that it can be
-		// checked again later (in the test suite, specifically)
-		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
-
-		// convert data if not already a string
-		if ( s.data && s.processData && typeof s.data != "string" )
-			s.data = jQuery.param(s.data);
-
-		// Handle JSONP Parameter Callbacks
-		if ( s.dataType == "jsonp" ) {
-			if ( s.type.toLowerCase() == "get" ) {
-				if ( !s.url.match(jsre) )
-					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
-			} else if ( !s.data || !s.data.match(jsre) )
-				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
-			s.dataType = "json";
-		}
-
-		// Build temporary JSONP function
-		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
-			jsonp = "jsonp" + jsc++;
-
-			// Replace the =? sequence both in the query string and the data
-			if ( s.data )
-				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
-			s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
-			// We need to make sure
-			// that a JSONP style response is executed properly
-			s.dataType = "script";
-
-			// Handle JSONP-style loading
-			window[ jsonp ] = function(tmp){
-				data = tmp;
-				success();
-				complete();
-				// Garbage collect
-				window[ jsonp ] = undefined;
-				try{ delete window[ jsonp ]; } catch(e){}
-				if ( head )
-					head.removeChild( script );
-			};
-		}
-
-		if ( s.dataType == "script" && s.cache == null )
-			s.cache = false;
-
-		if ( s.cache === false && s.type.toLowerCase() == "get" ) {
-			var ts = (new Date()).getTime();
-			// try replacing _= if it is there
-			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
-			// if nothing was replaced, add timestamp to the end
-			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
-		}
-
-		// If data is available, append data to url for get requests
-		if ( s.data && s.type.toLowerCase() == "get" ) {
-			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
-
-			// IE likes to send both get and post data, prevent this
-			s.data = null;
-		}
-
-		// Watch for a new set of requests
-		if ( s.global && ! jQuery.active++ )
-			jQuery.event.trigger( "ajaxStart" );
-
-		// If we're requesting a remote document
-		// and trying to load JSON or Script with a GET
-		if ( (!s.url.indexOf("http") || !s.url.indexOf("//")) && s.dataType == "script" && s.type.toLowerCase() == "get" ) {
-			var head = document.getElementsByTagName("head")[0];
-			var script = document.createElement("script");
-			script.src = s.url;
-			if (s.scriptCharset)
-				script.charset = s.scriptCharset;
-
-			// Handle Script loading
-			if ( !jsonp ) {
-				var done = false;
-
-				// Attach handlers for all browsers
-				script.onload = script.onreadystatechange = function(){
-					if ( !done && (!this.readyState || 
-							this.readyState == "loaded" || this.readyState == "complete") ) {
-						done = true;
-						success();
-						complete();
-						head.removeChild( script );
-					}
-				};
-			}
-
-			head.appendChild(script);
-
-			// We handle everything using the script element injection
-			return undefined;
-		}
-
-		var requestDone = false;
-
-		// Create the request object; Microsoft failed to properly
-		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
-		var xml = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
-
-		// Open the socket
-		xml.open(s.type, s.url, s.async, s.username, s.password);
-
-		// Need an extra try/catch for cross domain requests in Firefox 3
-		try {
-			// Set the correct header, if data is being sent
-			if ( s.data )
-				xml.setRequestHeader("Content-Type", s.contentType);
-
-			// Set the If-Modified-Since header, if ifModified mode.
-			if ( s.ifModified )
-				xml.setRequestHeader("If-Modified-Since",
-					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
-
-			// Set header so the called script knows that it's an XMLHttpRequest
-			xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-
-			// Set the Accepts header for the server, depending on the dataType
-			xml.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
-				s.accepts[ s.dataType ] + ", */*" :
-				s.accepts._default );
-		} catch(e){}
-
-		// Allow custom headers/mimetypes
-		if ( s.beforeSend )
-			s.beforeSend(xml);
-			
-		if ( s.global )
-			jQuery.event.trigger("ajaxSend", [xml, s]);
-
-		// Wait for a response to come back
-		var onreadystatechange = function(isTimeout){
-			// The transfer is complete and the data is available, or the request timed out
-			if ( !requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
-				requestDone = true;
-				
-				// clear poll interval
-				if (ival) {
-					clearInterval(ival);
-					ival = null;
-				}
-				
-				status = isTimeout == "timeout" && "timeout" ||
-					!jQuery.httpSuccess( xml ) && "error" ||
-					s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
-					"success";
-
-				if ( status == "success" ) {
-					// Watch for, and catch, XML document parse errors
-					try {
-						// process the data (runs the xml through httpData regardless of callback)
-						data = jQuery.httpData( xml, s.dataType );
-					} catch(e) {
-						status = "parsererror";
-					}
-				}
-
-				// Make sure that the request was successful or notmodified
-				if ( status == "success" ) {
-					// Cache Last-Modified header, if ifModified mode.
-					var modRes;
-					try {
-						modRes = xml.getResponseHeader("Last-Modified");
-					} catch(e) {} // swallow exception thrown by FF if header is not available
-	
-					if ( s.ifModified && modRes )
-						jQuery.lastModified[s.url] = modRes;
-
-					// JSONP handles its own success callback
-					if ( !jsonp )
-						success();	
-				} else
-					jQuery.handleError(s, xml, status);
-
-				// Fire the complete handlers
-				complete();
-
-				// Stop memory leaks
-				if ( s.async )
-					xml = null;
-			}
-		};
-		
-		if ( s.async ) {
-			// don't attach the handler to the request, just poll it instead
-			var ival = setInterval(onreadystatechange, 13); 
-
-			// Timeout checker
-			if ( s.timeout > 0 )
-				setTimeout(function(){
-					// Check to see if the request is still happening
-					if ( xml ) {
-						// Cancel the request
-						xml.abort();
-	
-						if( !requestDone )
-							onreadystatechange( "timeout" );
-					}
-				}, s.timeout);
-		}
-			
-		// Send the data
-		try {
-			xml.send(s.data);
-		} catch(e) {
-			jQuery.handleError(s, xml, null, e);
-		}
-		
-		// firefox 1.5 doesn't fire statechange for sync requests
-		if ( !s.async )
-			onreadystatechange();
-
-		function success(){
-			// If a local callback was specified, fire it and pass it the data
-			if ( s.success )
-				s.success( data, status );
-
-			// Fire the global callback
-			if ( s.global )
-				jQuery.event.trigger( "ajaxSuccess", [xml, s] );
-		}
-
-		function complete(){
-			// Process result
-			if ( s.complete )
-				s.complete(xml, status);
-
-			// The request was completed
-			if ( s.global )
-				jQuery.event.trigger( "ajaxComplete", [xml, s] );
-
-			// Handle the global AJAX counter
-			if ( s.global && ! --jQuery.active )
-				jQuery.event.trigger( "ajaxStop" );
-		}
-		
-		// return XMLHttpRequest to allow aborting the request etc.
-		return xml;
-	},
-
-	handleError: function( s, xml, status, e ) {
-		// If a local callback was specified, fire it
-		if ( s.error ) s.error( xml, status, e );
-
-		// Fire the global callback
-		if ( s.global )
-			jQuery.event.trigger( "ajaxError", [xml, s, e] );
-	},
-
-	// Counter for holding the number of active queries
-	active: 0,
-
-	// Determines if an XMLHttpRequest was successful or not
-	httpSuccess: function( r ) {
-		try {
-			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
-			return !r.status && location.protocol == "file:" ||
-				( r.status >= 200 && r.status < 300 ) || r.status == 304 || r.status == 1223 ||
-				jQuery.browser.safari && r.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	// Determines if an XMLHttpRequest returns NotModified
-	httpNotModified: function( xml, url ) {
-		try {
-			var xmlRes = xml.getResponseHeader("Last-Modified");
-
-			// Firefox always returns 200. check Last-Modified date
-			return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
-				jQuery.browser.safari && xml.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	httpData: function( r, type ) {
-		var ct = r.getResponseHeader("content-type");
-		var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
-		var data = xml ? r.responseXML : r.responseText;
-
-		if ( xml && data.documentElement.tagName == "parsererror" )
-			throw "parsererror";
-
-		// If the type is "script", eval it in global context
-		if ( type == "script" )
-			jQuery.globalEval( data );
-
-		// Get the JavaScript object, if JSON is used.
-		if ( type == "json" )
-			data = eval("(" + data + ")");
-
-		return data;
-	},
-
-	// Serialize an array of form elements or a set of
-	// key/values into a query string
-	param: function( a ) {
-		var s = [];
-
-		// If an array was passed in, assume that it is an array
-		// of form elements
-		if ( a.constructor == Array || a.jquery )
-			// Serialize the form elements
-			jQuery.each( a, function(){
-				s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
-			});
-
-		// Otherwise, assume that it's an object of key/value pairs
-		else
-			// Serialize the key/values
-			for ( var j in a )
-				// If the value is an array then the key names need to be repeated
-				if ( a[j] && a[j].constructor == Array )
-					jQuery.each( a[j], function(){
-						s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
-					});
-				else
-					s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
-
-		// Return the resulting serialization
-		return s.join("&").replace(/%20/g, "+");
-	}
-
-});
-jQuery.fn.extend({
-	show: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "show", width: "show", opacity: "show"
-			}, speed, callback) :
-			
-			this.filter(":hidden").each(function(){
-				this.style.display = this.oldblock || "";
-				if ( jQuery.css(this,"display") == "none" ) {
-					var elem = jQuery("<" + this.tagName + " />").appendTo("body");
-					this.style.display = elem.css("display");
-					// handle an edge condition where css is - div { display:none; } or similar
-					if (this.style.display == "none")
-						this.style.display = "block";
-					elem.remove();
-				}
-			}).end();
-	},
-	
-	hide: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "hide", width: "hide", opacity: "hide"
-			}, speed, callback) :
-			
-			this.filter(":visible").each(function(){
-				this.oldblock = this.oldblock || jQuery.css(this,"display");
-				this.style.display = "none";
-			}).end();
-	},
-
-	// Save the old toggle function
-	_toggle: jQuery.fn.toggle,
-	
-	toggle: function( fn, fn2 ){
-		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
-			this._toggle( fn, fn2 ) :
-			fn ?
-				this.animate({
-					height: "toggle", width: "toggle", opacity: "toggle"
-				}, fn, fn2) :
-				this.each(function(){
-					jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
-				});
-	},
-	
-	slideDown: function(speed,callback){
-		return this.animate({height: "show"}, speed, callback);
-	},
-	
-	slideUp: function(speed,callback){
-		return this.animate({height: "hide"}, speed, callback);
-	},
-
-	slideToggle: function(speed, callback){
-		return this.animate({height: "toggle"}, speed, callback);
-	},
-	
-	fadeIn: function(speed, callback){
-		return this.animate({opacity: "show"}, speed, callback);
-	},
-	
-	fadeOut: function(speed, callback){
-		return this.animate({opacity: "hide"}, speed, callback);
-	},
-	
-	fadeTo: function(speed,to,callback){
-		return this.animate({opacity: to}, speed, callback);
-	},
-	
-	animate: function( prop, speed, easing, callback ) {
-		var optall = jQuery.speed(speed, easing, callback);
-
-		return this[ optall.queue === false ? "each" : "queue" ](function(){
-			if ( this.nodeType != 1)
-				return false;
-
-			var opt = jQuery.extend({}, optall);
-			var hidden = jQuery(this).is(":hidden"), self = this;
-			
-			for ( var p in prop ) {
-				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
-					return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
-
-				if ( p == "height" || p == "width" ) {
-					// Store display property
-					opt.display = jQuery.css(this, "display");
-
-					// Make sure that nothing sneaks out
-					opt.overflow = this.style.overflow;
-				}
-			}
-
-			if ( opt.overflow != null )
-				this.style.overflow = "hidden";
-
-			opt.curAnim = jQuery.extend({}, prop);
-			
-			jQuery.each( prop, function(name, val){
-				var e = new jQuery.fx( self, opt, name );
-
-				if ( /toggle|show|hide/.test(val) )
-					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
-				else {
-					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
-						start = e.cur(true) || 0;
-
-					if ( parts ) {
-						var end = parseFloat(parts[2]),
-							unit = parts[3] || "px";
-
-						// We need to compute starting value
-						if ( unit != "px" ) {
-							self.style[ name ] = (end || 1) + unit;
-							start = ((end || 1) / e.cur(true)) * start;
-							self.style[ name ] = start + unit;
-						}
-
-						// If a +=/-= token was provided, we're doing a relative animation
-						if ( parts[1] )
-							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
-
-						e.custom( start, end, unit );
-					} else
-						e.custom( start, val, "" );
-				}
-			});
-
-			// For JS strict compliance
-			return true;
-		});
-	},
-	
-	queue: function(type, fn){
-		if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
-			fn = type;
-			type = "fx";
-		}
-
-		if ( !type || (typeof type == "string" && !fn) )
-			return queue( this[0], type );
-
-		return this.each(function(){
-			if ( fn.constructor == Array )
-				queue(this, type, fn);
-			else {
-				queue(this, type).push( fn );
-			
-				if ( queue(this, type).length == 1 )
-					fn.apply(this);
-			}
-		});
-	},
-
-	stop: function(clearQueue, gotoEnd){
-		var timers = jQuery.timers;
-
-		if (clearQueue)
-			this.queue([]);
-
-		this.each(function(){
-			// go in reverse order so anything added to the queue during the loop is ignored
-			for ( var i = timers.length - 1; i >= 0; i-- )
-				if ( timers[i].elem == this ) {
-					if (gotoEnd)
-						// force the next step to be the last
-						timers[i](true);
-					timers.splice(i, 1);
-				}
-		});
-
-		// start the next in the queue if the last step wasn't forced
-		if (!gotoEnd)
-			this.dequeue();
-
-		return this;
-	}
-
-});
-
-var queue = function( elem, type, array ) {
-	if ( !elem )
-		return undefined;
-
-	type = type || "fx";
-
-	var q = jQuery.data( elem, type + "queue" );
-
-	if ( !q || array )
-		q = jQuery.data( elem, type + "queue", 
-			array ? jQuery.makeArray(array) : [] );
-
-	return q;
-};
-
-jQuery.fn.dequeue = function(type){
-	type = type || "fx";
-
-	return this.each(function(){
-		var q = queue(this, type);
-
-		q.shift();
-
-		if ( q.length )
-			q[0].apply( this );
-	});
-};
-
-jQuery.extend({
-	
-	speed: function(speed, easing, fn) {
-		var opt = speed && speed.constructor == Object ? speed : {
-			complete: fn || !fn && easing || 
-				jQuery.isFunction( speed ) && speed,
-			duration: speed,
-			easing: fn && easing || easing && easing.constructor != Function && easing
-		};
-
-		opt.duration = (opt.duration && opt.duration.constructor == Number ? 
-			opt.duration : 
-			{ slow: 600, fast: 200 }[opt.duration]) || 400;
-	
-		// Queueing
-		opt.old = opt.complete;
-		opt.complete = function(){
-			if ( opt.queue !== false )
-				jQuery(this).dequeue();
-			if ( jQuery.isFunction( opt.old ) )
-				opt.old.apply( this );
-		};
-	
-		return opt;
-	},
-	
-	easing: {
-		linear: function( p, n, firstNum, diff ) {
-			return firstNum + diff * p;
-		},
-		swing: function( p, n, firstNum, diff ) {
-			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
-		}
-	},
-	
-	timers: [],
-	timerId: null,
-
-	fx: function( elem, options, prop ){
-		this.options = options;
-		this.elem = elem;
-		this.prop = prop;
-
-		if ( !options.orig )
-			options.orig = {};
-	}
-
-});
-
-jQuery.fx.prototype = {
-
-	// Simple function for setting a style value
-	update: function(){
-		if ( this.options.step )
-			this.options.step.apply( this.elem, [ this.now, this ] );
-
-		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
-
-		// Set display property to block for height/width animations
-		if ( this.prop == "height" || this.prop == "width" )
-			this.elem.style.display = "block";
-	},
-
-	// Get the current size
-	cur: function(force){
-		if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
-			return this.elem[ this.prop ];
-
-		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
-		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
-	},
-
-	// Start an animation from one number to another
-	custom: function(from, to, unit){
-		this.startTime = (new Date()).getTime();
-		this.start = from;
-		this.end = to;
-		this.unit = unit || this.unit || "px";
-		this.now = this.start;
-		this.pos = this.state = 0;
-		this.update();
-
-		var self = this;
-		function t(gotoEnd){
-			return self.step(gotoEnd);
-		}
-
-		t.elem = this.elem;
-
-		jQuery.timers.push(t);
-
-		if ( jQuery.timerId == null ) {
-			jQuery.timerId = setInterval(function(){
-				var timers = jQuery.timers;
-				
-				for ( var i = 0; i < timers.length; i++ )
-					if ( !timers[i]() )
-						timers.splice(i--, 1);
-
-				if ( !timers.length ) {
-					clearInterval( jQuery.timerId );
-					jQuery.timerId = null;
-				}
-			}, 13);
-		}
-	},
-
-	// Simple 'show' function
-	show: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.show = true;
-
-		// Begin the animation
-		this.custom(0, this.cur());
-
-		// Make sure that we start at a small width/height to avoid any
-		// flash of content
-		if ( this.prop == "width" || this.prop == "height" )
-			this.elem.style[this.prop] = "1px";
-		
-		// Start by showing the element
-		jQuery(this.elem).show();
-	},
-
-	// Simple 'hide' function
-	hide: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.hide = true;
-
-		// Begin the animation
-		this.custom(this.cur(), 0);
-	},
-
-	// Each step of an animation
-	step: function(gotoEnd){
-		var t = (new Date()).getTime();
-
-		if ( gotoEnd || t > this.options.duration + this.startTime ) {
-			this.now = this.end;
-			this.pos = this.state = 1;
-			this.update();
-
-			this.options.curAnim[ this.prop ] = true;
-
-			var done = true;
-			for ( var i in this.options.curAnim )
-				if ( this.options.curAnim[i] !== true )
-					done = false;
-
-			if ( done ) {
-				if ( this.options.display != null ) {
-					// Reset the overflow
-					this.elem.style.overflow = this.options.overflow;
-				
-					// Reset the display
-					this.elem.style.display = this.options.display;
-					if ( jQuery.css(this.elem, "display") == "none" )
-						this.elem.style.display = "block";
-				}
-
-				// Hide the element if the "hide" operation was done
-				if ( this.options.hide )
-					this.elem.style.display = "none";
-
-				// Reset the properties, if the item has been hidden or shown
-				if ( this.options.hide || this.options.show )
-					for ( var p in this.options.curAnim )
-						jQuery.attr(this.elem.style, p, this.options.orig[p]);
-			}
-
-			// If a callback was provided, execute it
-			if ( done && jQuery.isFunction( this.options.complete ) )
-				// Execute the complete function
-				this.options.complete.apply( this.elem );
-
-			return false;
-		} else {
-			var n = t - this.startTime;
-			this.state = n / this.options.duration;
-
-			// Perform the easing function, defaults to swing
-			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
-			this.now = this.start + ((this.end - this.start) * this.pos);
-
-			// Perform the next step of the animation
-			this.update();
-		}
-
-		return true;
-	}
-
-};
-
-jQuery.fx.step = {
-	scrollLeft: function(fx){
-		fx.elem.scrollLeft = fx.now;
-	},
-
-	scrollTop: function(fx){
-		fx.elem.scrollTop = fx.now;
-	},
-
-	opacity: function(fx){
-		jQuery.attr(fx.elem.style, "opacity", fx.now);
-	},
-
-	_default: function(fx){
-		fx.elem.style[ fx.prop ] = fx.now + fx.unit;
-	}
-};
-// The Offset Method
-// Originally By Brandon Aaron, part of the Dimension Plugin
-// http://jquery.com/plugins/project/dimensions
-jQuery.fn.offset = function() {
-	var left = 0, top = 0, elem = this[0], results;
-	
-	if ( elem ) with ( jQuery.browser ) {
-		var parent       = elem.parentNode, 
-		    offsetChild  = elem,
-		    offsetParent = elem.offsetParent, 
-		    doc          = elem.ownerDocument,
-		    safari2      = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
-		    fixed        = jQuery.css(elem, "position") == "fixed";
-	
-		// Use getBoundingClientRect if available
-		if ( elem.getBoundingClientRect ) {
-			var box = elem.getBoundingClientRect();
-		
-			// Add the document scroll offsets
-			add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-				box.top  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		
-			// IE adds the HTML element's border, by default it is medium which is 2px
-			// IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; }
-			// IE 7 standards mode, the border is always 2px
-			// This border/offset is typically represented by the clientLeft and clientTop properties
-			// However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
-			// Therefore this method will be off by 2px in IE while in quirksmode
-			add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
-	
-		// Otherwise loop through the offsetParents and parentNodes
-		} else {
-		
-			// Initial element offsets
-			add( elem.offsetLeft, elem.offsetTop );
-			
-			// Get parent offsets
-			while ( offsetParent ) {
-				// Add offsetParent offsets
-				add( offsetParent.offsetLeft, offsetParent.offsetTop );
-			
-				// Mozilla and Safari > 2 does not include the border on offset parents
-				// However Mozilla adds the border for table or table cells
-				if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
-					border( offsetParent );
-					
-				// Add the document scroll offsets if position is fixed on any offsetParent
-				if ( !fixed && jQuery.css(offsetParent, "position") == "fixed" )
-					fixed = true;
-			
-				// Set offsetChild to previous offsetParent unless it is the body element
-				offsetChild  = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
-				// Get next offsetParent
-				offsetParent = offsetParent.offsetParent;
-			}
-		
-			// Get parent scroll offsets
-			while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) ) {
-				// Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
-				if ( !/^inline|table.*$/i.test(jQuery.css(parent, "display")) )
-					// Subtract parent scroll offsets
-					add( -parent.scrollLeft, -parent.scrollTop );
-			
-				// Mozilla does not add the border for a parent that has overflow != visible
-				if ( mozilla && jQuery.css(parent, "overflow") != "visible" )
-					border( parent );
-			
-				// Get next parent
-				parent = parent.parentNode;
-			}
-		
-			// Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
-			// Mozilla doubles body offsets with a non-absolutely positioned offsetChild
-			if ( (safari2 && (fixed || jQuery.css(offsetChild, "position") == "absolute")) || 
-				(mozilla && jQuery.css(offsetChild, "position") != "absolute") )
-					add( -doc.body.offsetLeft, -doc.body.offsetTop );
-			
-			// Add the document scroll offsets if position is fixed
-			if ( fixed )
-				add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-					Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		}
-
-		// Return an object with top and left properties
-		results = { top: top, left: left };
-	}
-
-	function border(elem) {
-		add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) );
-	}
-
-	function add(l, t) {
-		left += parseInt(l) || 0;
-		top += parseInt(t) || 0;
-	}
-
-	return results;
-};
-})();
@@ -0,0 +1,29 @@
+(function() {
+
+var proto = Test.Base.newSubclass('Test.Jemplate');
+
+proto = Test.Jemplate.Filter.prototype;
+
+proto.jemplate_process = function(content, block) {
+    var template;
+    if (content.match(/\n/))
+        template = content.split(/\n/)[0];
+    else
+        template = content;
+
+    var j = new Jemplate();
+    var data = block.data.context;
+    var result = Jemplate.process(template, data);
+    return result;
+}
+
+proto.raw_context = function( content, block ) {
+    try {
+        var context = eval("("+content+")");
+        block.data.context = context;
+    } catch(e) {
+        alert(e);
+    }
+}
+
+})();
@@ -0,0 +1,18 @@
+if (typeof dummy == 'undefined') {
+    dummy = function() {
+        this.context = arguments[0];
+        this.what = arguments[1];
+    };
+}
+
+dummy.prototype.simple = function() {
+    return 'This text came from the plugin';
+}
+
+dummy.prototype.params = function(one, two) {
+    return 'params: '+one+', '+two;
+}
+
+dummy.prototype.get_what = function() {
+    return 'what: ' + this.what;
+}
@@ -1,53 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(2);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test list methods
---- jemplate
-list.html
-[% SET a1 = [ 'one', 'two', 'three'] -%]
-[% CALL a1.push('four') -%]
-[%- a1.first() %] - [% a1.last() %]
-[% a1.grep('o').join('/') %]
-[% a1.max() %]+[% a1.size() %]
-[% SET a2 = a1.reverse -%]
-[% a2.join('^') %]
-[% a2.slice(1, 3).join('*') %]
-[% SET a3 = [ 5, 9, 'x', 17, 9, 33, 12, 'x', 5] -%]
-[% a3.unique().join(',') %]
-[% a1.unshift('zero').sort().join('!') %]
-[% CALL a1.shift(); CALL a1.pop(); a1.join('_') %]
-[% CALL a3.splice(2,1); CALL a3.splice(-2,1); a3.nsort.join('~') %]
-[% SET a4 = [11, 22, 33] -%]
-[% SET a5 = [44, 55, 66] -%]
-[% SET a6 = [77, 88, 99] -%]
-[% SET a7 = a4.merge(a5, 'foo', a6) -%]
-[% a7.join("'") %]
---- output
-one - four
-one/two/four
-3+4
-four^three^two^one
-three*two
-5,9,x,17,33,12
-four!one!three!two!zero
-one_three_two
-5~5~9~9~12~17~33
-11'22'33'44'55'66'foo'77'88'99
-=== Test sort of hash
---- jemplate
-list_of_hash.html
-[% SET ll = [ {'a' => 9}, {'a' =>  1}, {'a' => 3} ] -%]
-[%- FOR hash = ll.sort('a') -%]
-[%- hash.a -%]:
-[%- END -%]
---- output
-1:3:9:
-*/
@@ -1,49 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(2);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== INCLUDE should localize
---- context
-{"thing":"foo"}
---- jemplate
-localise1.html
-[% thing %]
-[% SET thing = 'bar' -%]
-[% thing %]
-[% INCLUDE myblock -%]
-[% thing %]
-[% BLOCK myblock -%]
-[% SET thing = 'baz' -%]
-[% END -%]
---- output
-foo
-bar
-bar
-
-=== PROCESS should not localize
---- context
-{"thing":"foo"}
---- jemplate
-localise2.html
-[% thing %]
-[% SET thing = 'bar' -%]
-[% thing %]
-[% PROCESS myblock -%]
-[% thing %]
-[% BLOCK myblock -%]
-[% SET thing = 'baz' -%]
-[% END -%]
---- output
-foo
-bar
-baz
-
-*/
@@ -1,83 +0,0 @@
-
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(5);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test loop.index
---- jemplate
-loop1.html
-[%- FOR i IN [0,1,2,3] -%]
-[%- i -%]:[%- loop.index %]
-[% END -%]
---- output
-0:0
-1:1
-2:2
-3:3
-
-=== Test loop.first && loop.last
---- jemplate
-loop2.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- IF loop.first -%]
-[start]
-    [%- END -%]
-    [%- i -%]
-    [%- IF loop.last -%]
-[end]
-    [%- END -%]
-[%- END %]
---- output
-[start]0123[end]
-
-=== Test loop.count
---- jemplate
-loop3.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.count%]
-[% END -%]
---- output
-0:1
-1:2
-2:3
-3:4
-
-=== Test loop.size && loop.max
---- jemplate
-loop4.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.size%]:[%-loop.max%]
-[% END -%]
---- output
-0:4:3
-1:4:3
-2:4:3
-3:4:3
-
-=== Test loop.prev && loop.next
---- jemplate
-loop5.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
-[% END -%]
-[%- FOR i IN {a => 0, b => 1, c => 3, d => 4 } -%]
-    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
-[% END -%]
---- output
-0::1
-1:0:2
-2:1:3
-3:2:
-a::b
-b:a:c
-c:b:d
-d:c:
-
-*/
@@ -1,12 +0,0 @@
--- incomplete --
-
-./mangle.pl  --context=ctx.tst < $TT_HOME/t/parser.t > newtest.t.js
-
-- mangle.pl tries its best to generate Test.Data based on TT test suite
-- $TT_HOME/t/parser.t is the TT test suite being extracted
-- ctx.tst is a manual extract of the $context (often called $params) object
-  used in TT (in $TT_HOME/t/parser.t)
-- newtest.t.js is the Test.Data file generated
-- in ./jemplates is the 'jemplates' files defined in the jemplate block
-
-
@@ -1,11 +0,0 @@
-my $obj =  {
-    metavars => [ qw( foo bar baz qux wiz waz woz ) ],
-    people   => [ { id => 'tom',   name => 'Tom' },
-                  { id => 'dick',  name => 'Richard' },
-                  { id => 'larry', name => 'Larry' },
-                  ],
-    primes    => [ 13, 11, 17, 19, 2, 3, 5, 7 ],
-    phones    => { 3141 => 'Leon', 5131 => 'Andy', 4131 => 'Simon' },
-    groceries => { 'Flour' => 3, 'Milk' => 1, 'Peanut Butter' => 21 },
-    duplicates => [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5],
-};
@@ -1,166 +0,0 @@
-#! /usr/bin/perl -w
-# $Id$
-## Most parts are ripped from Template::Test and thus (c) Andy Wardley
-
-# This is an utility script to extract Test cases (from the DATA secion) 
-# from the TT test suite # in order to reuse them in Jemplate.
-#
-# Limitation : it's not currently possible to automatically generate 
-# Test.Data compatible suite because TT test suite also  relies on 
-# Perl manipulation. (context, post-processing, My::Object)
-
-use strict;
-use warnings;
-use Template; 
-use IO::All;
-use File::Spec;
-use Getopt::Long;
-use Pod::Usage;
- 
- 
-my $context  = '';
-my $ctx_file = '';
-my $mode     = "perl";
-my $help     = 0;
-
-GetOptions('help|?' => \$help, "context=s" => \$ctx_file, "mode=s", \$mode) 
-    or pod2usage(2); 
-pod2usage(1) if $help;
-
-our $tt = Template->new;
-
-if (-e $ctx_file) {
-    my $content = io($ctx_file)->slurp;
-    if ($mode eq 'perl') {
-        require JSON;
-        my $json = JSON->new(); #pretty => 1, delimiter => 1);
-        $context = $json->objToJson(eval $content );
-    } else {
-        #assume it's raw context
-        $context = $content;
-    }
-}
-
-my @data = io('-')->separator('__DATA__')->slurp;
-my $data = pop @data; 
-my $tests = extract($data);
-create_jemplates($tests);
-generate_testfile($tests, $context);
-
-# Create the Test.Data test file 
-sub generate_testfile {
-    my ($tests, $context) = @_;
-    # reuse tt itself ;)
-    $tt->process('test-data.tmpl.tt', 
-        { tests => $tests, context => $context, name => 'newtest' })
-        or die $tt->error;
-}
-
-# create jemplates on disk based on the 'input' of the tests
-sub create_jemplates {
-    my $tests = shift;
-    for my $t (@$tests) {
-        my $name = $t->{name} || "";
-        my $filename = File::Spec->catfile('jemplates', dirify($name).'.html');
-        my $content = delete $t->{input}; 
-        $content > io($filename);
-        $t->{filename} = $filename;
-    }
-}
-
-# Generate a cleaner filename from the test name
-sub dirify {
-    my $name = shift;
-    $name =~ s/ /-/g;
-    $name =~ s/[^\w\.-]/_/g;
-    return $name;
-}
-
-sub extract {
-    my ($src) = @_;
-    my $input;
-    eval {
-        local $/ = undef;
-        $input = ref $src ? <$src> : $src;
-    };
-    if ($@) {
-        warn "Cannot read input text from $src\n";
-        return undef;
-    }
-
-    # remove any comment lines
-    $input =~ s/^#.*?\n//gm;
-
-    # remove anything before '-- start --' and/or after '-- stop --'
-    $input = $' if $input =~ /\s*--\s*start\s*--\s*/;
-    $input = $` if $input =~ /\s*--\s*stop\s*--\s*/;
-
-    my @tests = split(/^\s*--\s*test\s*--\s*\n/im, $input);
-
-    # if the first line of the file was '--test--' (optional) then the 
-    # first test will be empty and can be discarded
-    shift(@tests) if $tests[0] =~ /^\s*$/;
-
-    my @suite = ();
-    my $count = 0;
-    # the remaining tests are defined in @tests...
-    foreach my $input (@tests) {
-        $count++;
-        my $name = '';
-
-        if ($input =~ s/^\s*-- name:? (.*?) --\s*\n//im) {
-            $name = $1; 
-        } else {
-            $name = "test $count";
-        }
-
-        # split input by a line like "-- expect --"
-        my $expect;
-        ($input, $expect) = 
-            split(/^\s*--\s*expect\s*--\s*\n/im, $input);
-        $expect = '' 
-            unless defined $expect;
-
-        # input text may be prefixed with "-- use name --" to indicate a
-        # Template object in the $ttproc hash which we should use
-        if ($input =~ s/^\s*--\s*use\s+(\S+)\s*--\s*\n//im) {
-            warn "ignored 'use' thing";
-            next;
-        }
-
-        # another hack: if the '-- expect --' section starts with 
-        # '-- process --' then we process the expected output 
-        # before comparing it with the generated output.  This is
-        # slightly twisted but it makes it possible to run tests 
-        # where the expected output isn't static.  See t/date.t for
-        # an example.
-
-        if ($expect =~ s/^\s*--+\s*process\s*--+\s*\n//im) {
-            warn "ignored the 'process' hack";
-            next;
-        }; 
-        push @suite, { input => $input, expect => $expect, name => $name };
-    }
-    return \@suite;
-}
-
-__END__
-
-=head1 NAME
-
-find a name if this script is useful
-
-=head1 SYNOPSIS
-
-mangle.pl [--context file] [--mode perl|other] < input
-
- Options:
-   --help            this help message
-   --context=        context file to use
-   --mode=           of the context
-
-=head1 DESCRIPTION
-
-stub. to write.
-
-=cut
@@ -1,22 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan([% tests.size %]);
-t.filters(filters);
-t.spec('[% name %].t.js'); 
-t.run_is('jemplate', 'output');
-
-/* Test
-[%- FOR t = tests %]
-=== [% t.name %]
---- context
-[% context %]
---- jemplate: [% t.filename %]
---- output
-[% t.expect %]
-[%- END %]
-*/
@@ -1,69 +0,0 @@
-function TheName(name) {
-    this.name = name;
-}
-
-TheName.prototype.getName = function() {
-   return this.name;
-}
-
-TheName.prototype.setName = function(name) {
-   this.name = name;
-}
-
-var theName = new TheName('larry');
-
-// Begin test
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(4);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Basic hash
---- jemplate
-myhashname.html
-[%- JAVASCRIPT -%]
-stash.set( "object", { name: 'Wally' } );
-[%- END -%]
-Hello, [% object.name %]
---- output
-Hello, Wally
-
-=== Object attribute
---- jemplate
-myobjectnameattr.html
-[%- JAVASCRIPT -%]
-stash.set( "object", new TheName('Larry') );
-[%- END -%]
-Hello, [% object.name %]
---- output
-Hello, Larry
-
-=== Object accessor
---- jemplate
-myobjectnameac.html
-[%- JAVASCRIPT -%]
-var obj = new TheName('Bally');
-stash.set( "objectName", obj.getName() );
-[%- END -%]
-Hello, [% objectName %]
---- output
-Hello, Bally
-
-=== Basic accessor2
---- jemplate
-myobjectnameac2.html
-[%- JAVASCRIPT -%]
-stash.set( "object", new TheName('Mally') );
-[%- END -%]
-Hello, [% object.getName() %]
---- output
-Hello, Mally
-
-*/
@@ -0,0 +1,16 @@
+.PHONY: deps run-files
+
+INGY_JSAN_CODE=../../../ingy/jsan
+
+all: run-files
+
+deps: run
+	cp -r $(INGY_JSAN_CODE)/Test-Base/sample/run/lib/ run/lib/
+	cp -r $(INGY_JSAN_CODE)/Test-Base/sample/run/bin/ run/bin/
+	cp -r $(INGY_JSAN_CODE)/Test-Base/sample/run/template/ run/template/
+
+run:
+	mkdir $@
+
+run-files:
+	make -C run clean all
@@ -0,0 +1,12 @@
+-- incomplete --
+
+./mangle.pl  --context=ctx.tst < $TT_HOME/t/parser.t > newtest.t.js
+
+- mangle.pl tries its best to generate Test.Data based on TT test suite
+- $TT_HOME/t/parser.t is the TT test suite being extracted
+- ctx.tst is a manual extract of the $context (often called $params) object
+  used in TT (in $TT_HOME/t/parser.t)
+- newtest.t.js is the Test.Data file generated
+- in ./jemplates is the 'jemplates' files defined in the jemplate block
+
+
@@ -0,0 +1,11 @@
+my $obj =  {
+    metavars => [ qw( foo bar baz qux wiz waz woz ) ],
+    people   => [ { id => 'tom',   name => 'Tom' },
+                  { id => 'dick',  name => 'Richard' },
+                  { id => 'larry', name => 'Larry' },
+                  ],
+    primes    => [ 13, 11, 17, 19, 2, 3, 5, 7 ],
+    phones    => { 3141 => 'Leon', 5131 => 'Andy', 4131 => 'Simon' },
+    groceries => { 'Flour' => 3, 'Milk' => 1, 'Peanut Butter' => 21 },
+    duplicates => [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5],
+};
@@ -0,0 +1,166 @@
+#! /usr/bin/perl -w
+# $Id$
+## Most parts are ripped from Template::Test and thus (c) Andy Wardley
+
+# This is an utility script to extract Test cases (from the DATA secion) 
+# from the TT test suite # in order to reuse them in Jemplate.
+#
+# Limitation : it's not currently possible to automatically generate 
+# Test.Data compatible suite because TT test suite also  relies on 
+# Perl manipulation. (context, post-processing, My::Object)
+
+use strict;
+use warnings;
+use Template; 
+use IO::All;
+use File::Spec;
+use Getopt::Long;
+use Pod::Usage;
+ 
+ 
+my $context  = '';
+my $ctx_file = '';
+my $mode     = "perl";
+my $help     = 0;
+
+GetOptions('help|?' => \$help, "context=s" => \$ctx_file, "mode=s", \$mode) 
+    or pod2usage(2); 
+pod2usage(1) if $help;
+
+our $tt = Template->new;
+
+if (-e $ctx_file) {
+    my $content = io($ctx_file)->slurp;
+    if ($mode eq 'perl') {
+        require JSON;
+        my $json = JSON->new(); #pretty => 1, delimiter => 1);
+        $context = $json->objToJson(eval $content );
+    } else {
+        #assume it's raw context
+        $context = $content;
+    }
+}
+
+my @data = io('-')->separator('__DATA__')->slurp;
+my $data = pop @data; 
+my $tests = extract($data);
+create_jemplates($tests);
+generate_testfile($tests, $context);
+
+# Create the Test.Data test file 
+sub generate_testfile {
+    my ($tests, $context) = @_;
+    # reuse tt itself ;)
+    $tt->process('test-data.tmpl.tt', 
+        { tests => $tests, context => $context, name => 'newtest' })
+        or die $tt->error;
+}
+
+# create jemplates on disk based on the 'input' of the tests
+sub create_jemplates {
+    my $tests = shift;
+    for my $t (@$tests) {
+        my $name = $t->{name} || "";
+        my $filename = File::Spec->catfile('jemplates', dirify($name).'.html');
+        my $content = delete $t->{input}; 
+        $content > io($filename);
+        $t->{filename} = $filename;
+    }
+}
+
+# Generate a cleaner filename from the test name
+sub dirify {
+    my $name = shift;
+    $name =~ s/ /-/g;
+    $name =~ s/[^\w\.-]/_/g;
+    return $name;
+}
+
+sub extract {
+    my ($src) = @_;
+    my $input;
+    eval {
+        local $/ = undef;
+        $input = ref $src ? <$src> : $src;
+    };
+    if ($@) {
+        warn "Cannot read input text from $src\n";
+        return undef;
+    }
+
+    # remove any comment lines
+    $input =~ s/^#.*?\n//gm;
+
+    # remove anything before '-- start --' and/or after '-- stop --'
+    $input = $' if $input =~ /\s*--\s*start\s*--\s*/;
+    $input = $` if $input =~ /\s*--\s*stop\s*--\s*/;
+
+    my @tests = split(/^\s*--\s*test\s*--\s*\n/im, $input);
+
+    # if the first line of the file was '--test--' (optional) then the 
+    # first test will be empty and can be discarded
+    shift(@tests) if $tests[0] =~ /^\s*$/;
+
+    my @suite = ();
+    my $count = 0;
+    # the remaining tests are defined in @tests...
+    foreach my $input (@tests) {
+        $count++;
+        my $name = '';
+
+        if ($input =~ s/^\s*-- name:? (.*?) --\s*\n//im) {
+            $name = $1; 
+        } else {
+            $name = "test $count";
+        }
+
+        # split input by a line like "-- expect --"
+        my $expect;
+        ($input, $expect) = 
+            split(/^\s*--\s*expect\s*--\s*\n/im, $input);
+        $expect = '' 
+            unless defined $expect;
+
+        # input text may be prefixed with "-- use name --" to indicate a
+        # Template object in the $ttproc hash which we should use
+        if ($input =~ s/^\s*--\s*use\s+(\S+)\s*--\s*\n//im) {
+            warn "ignored 'use' thing";
+            next;
+        }
+
+        # another hack: if the '-- expect --' section starts with 
+        # '-- process --' then we process the expected output 
+        # before comparing it with the generated output.  This is
+        # slightly twisted but it makes it possible to run tests 
+        # where the expected output isn't static.  See t/date.t for
+        # an example.
+
+        if ($expect =~ s/^\s*--+\s*process\s*--+\s*\n//im) {
+            warn "ignored the 'process' hack";
+            next;
+        }; 
+        push @suite, { input => $input, expect => $expect, name => $name };
+    }
+    return \@suite;
+}
+
+__END__
+
+=head1 NAME
+
+find a name if this script is useful
+
+=head1 SYNOPSIS
+
+mangle.pl [--context file] [--mode perl|other] < input
+
+ Options:
+   --help            this help message
+   --context=        context file to use
+   --mode=           of the context
+
+=head1 DESCRIPTION
+
+stub. to write.
+
+=cut
@@ -0,0 +1,22 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate'
+};
+
+t.plan([% tests.size %]);
+t.filters(filters);
+t.spec('[% name %].t.js'); 
+t.run_is('jemplate', 'output');
+
+/* Test
+[%- FOR t = tests %]
+=== [% t.name %]
+--- context
+[% context %]
+--- jemplate: [% t.filename %]
+--- output
+[% t.expect %]
+[%- END %]
+*/
@@ -1,34 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test plugins
---- jemplate
-plugins_basic.html
-[% USE dummy('simple param') -%]
-[% dummy.simple %]
-#
-[% dummy.params('one', 'two') %]
-#
-[% dummy.what %]
-#
-[% dummy.get_what %]
-#
---- output
-This text came from the plugin
-#
-params: one, two
-#
-simple param
-#
-what: simple param
-#
-
-*/
@@ -1,858 +0,0 @@
-/*------------------------------------------------------------------------------
-Jemplate - Template Toolkit for JavaScript
-
-DESCRIPTION - This module provides the runtime JavaScript support for
-compiled Jemplate templates.
-
-AUTHOR - Ingy döt Net <ingy@cpan.org>
-
-Copyright 2006 Ingy döt Net. All Rights Reserved.
-
-This module is free software; you can redistribute it and/or
-modify it under the same terms as Perl itself.
-------------------------------------------------------------------------------*/
-
-//------------------------------------------------------------------------------
-// Main Jemplate class
-//------------------------------------------------------------------------------
-
-if (typeof Jemplate == 'undefined') {
-    var Jemplate = function() {
-        this.init.apply(this, arguments);
-    };
-}
-
-Jemplate.process = function() {
-    var jemplate = new Jemplate();
-    return jemplate.process.apply(jemplate, arguments);
-}
-
-;(function(){
-
-if (! Jemplate.templateMap)
-    Jemplate.templateMap = {};
-
-var proto = Jemplate.prototype = {};
-
-proto.init = function(config) {
-    this.config = config ||
-    {
-        AUTO_RESET: true,
-        BLOCKS: {},
-        CONTEXT: null,
-        DEBUG_UNDEF: false,
-        DEFAULT: null,
-        ERROR: null,
-        EVAL_JAVASCRIPT: false,
-        FILTERS: {},
-        INCLUDE_PATH: [''],
-        INTERPOLATE: false,
-        OUTPUT: null,
-        PLUGINS: {},
-        POST_PROCESS: [],
-        PRE_PROCESS: [],
-        PROCESS: null,
-        RECURSION: false,
-        STASH: null,
-        TOLERANT: null,
-        VARIABLES: {},
-        WRAPPER: []
-    };
-}
-
-proto.process = function(template, data, output) {
-    var context = this.config.CONTEXT || new Jemplate.Context();
-    context.config = this.config;
-
-    context.stash = this.config.STASH || new Jemplate.Stash();
-    context.stash.__config__ = this.config;
-
-    context.__filter__ = new Jemplate.Filter();
-    context.__filter__.config = this.config;
-
-    context.__plugin__ = new Jemplate.Plugin();
-    context.__plugin__.config = this.config;
-
-    var result;
-
-    var proc = function(input) {
-        try {
-            result = context.process(template, input);
-        }
-        catch(e) {
-            if (! String(e).match(/Jemplate\.STOP\n/))
-                throw(e);
-            result = e.toString().replace(/Jemplate\.STOP\n/, '');
-        }
-
-        if (typeof output == 'undefined')
-            return result;
-        if (typeof output == 'function') {
-            output(result);
-            return null;
-        }
-        if (typeof(output) == 'string' || output instanceof String) {
-            if (output.match(/^#[\w\-]+$/)) {
-                var id = output.replace(/^#/, '');
-                var element = document.getElementById(id);
-                if (typeof element == 'undefined')
-                    throw('No element found with id="' + id + '"');
-                element.innerHTML = result;
-                return null;
-            }
-        }
-        else {
-            output.innerHTML = result;
-            return null;
-        }
-
-        throw("Invalid arguments in call to Jemplate.process");
-
-        return 1;
-    }
-
-    if (typeof data == 'function')
-        data = data();
-    else if (typeof data == 'string') {
-//        Jemplate.Ajax.get(data, function(r) { proc(Jemplate.JSON.parse(r)) });
-        var url = data;
-        Jemplate.Ajax.processGet(url, function(data) { proc(data) });
-        return null;
-    }
-
-    return proc(data);
-}
-
-//------------------------------------------------------------------------------
-// Jemplate.Context class
-//------------------------------------------------------------------------------
-if (typeof Jemplate.Context == 'undefined')
-    Jemplate.Context = function() {};
-
-proto = Jemplate.Context.prototype;
-
-proto.include = function(template, args) {
-    return this.process(template, args, true);
-}
-
-proto.process = function(template, args, localise) {
-    if (localise)
-        this.stash.clone(args);
-    else
-        this.stash.update(args);
-    var func = Jemplate.templateMap[template];
-    if (typeof func == 'undefined')
-        throw('No Jemplate template named "' + template + '" available');
-    var output = func(this);
-    if (localise)
-        this.stash.declone();
-    return output;
-}
-
-proto.set_error = function(error, output) {
-    this._error = [error, output];
-    return error;
-}
-
-proto.plugin = function(name, args) {
-    if (typeof name == 'undefined')
-        throw "Unknown plugin name ':" + name + "'";
-
-    // The Context object (this) is passed as the first argument to the plugin.
-    return new window[name](this, args);
-}
-
-proto.filter = function(text, name, args) {
-    if (name == 'null')
-        name = "null_filter";
-    if (typeof this.__filter__.filters[name] == "function")
-        return this.__filter__.filters[name](text, args, this);
-    else
-        throw "Unknown filter name ':" + name + "'";
-}
-
-//------------------------------------------------------------------------------
-// Jemplate.Plugin class
-//------------------------------------------------------------------------------
-if (typeof Jemplate.Plugin == 'undefined') {
-    Jemplate.Plugin = function() { };
-}
-
-proto = Jemplate.Plugin.prototype;
-
-proto.plugins = {};
-
-//------------------------------------------------------------------------------
-// Jemplate.Filter class
-//------------------------------------------------------------------------------
-if (typeof Jemplate.Filter == 'undefined') {
-    Jemplate.Filter = function() { };
-}
-
-proto = Jemplate.Filter.prototype;
-
-proto.filters = {};
-
-proto.filters.null_filter = function(text) {
-    return '';
-}
-
-proto.filters.upper = function(text) {
-    return text.toUpperCase();
-}
-
-proto.filters.lower = function(text) {
-    return text.toLowerCase();
-}
-
-proto.filters.ucfirst = function(text) {
-    var first = text.charAt(0);
-    var rest = text.substr(1);
-    return first.toUpperCase() + rest;
-}
-
-proto.filters.lcfirst = function(text) {
-    var first = text.charAt(0);
-    var rest = text.substr(1);
-    return first.toLowerCase() + rest;
-}
-
-proto.filters.trim = function(text) {
-    return text.replace( /^\s+/g, "" ).replace( /\s+$/g, "" );
-}
-
-proto.filters.collapse = function(text) {
-    return text.replace( /^\s+/g, "" ).replace( /\s+$/g, "" ).replace(/\s+/, " ");
-}
-
-proto.filters.html = function(text) {
-    text = text.replace(/&/g, '&amp;');
-    text = text.replace(/</g, '&lt;');
-    text = text.replace(/>/g, '&gt;');
-    text = text.replace(/"/g, '&quot;'); // " end quote for emacs
-    return text;
-}
-
-proto.filters.html_para = function(text) {
-    var lines = text.split(/(?:\r?\n){2,}/);
-    return "<p>\n" + lines.join("\n</p>\n\n<p>\n") + "</p>\n";
-}
-
-proto.filters.html_break = function(text) {
-    return text.replace(/(\r?\n){2,}/g, "$1<br />$1<br />$1");
-}
-
-proto.filters.html_line_break = function(text) {
-    return text.replace(/(\r?\n)/g, "$1<br />$1");
-}
-
-proto.filters.uri = function(text) {
-    return encodeURI(text);
-}
-
-proto.filters.indent = function(text, args) {
-    var pad = args[0];
-    if (! text) return null;
-    if (typeof pad == 'undefined')
-        pad = 4;
-
-    var finalpad = '';
-    if (typeof pad == 'number' || String(pad).match(/^\d$/)) {
-        for (var i = 0; i < pad; i++) {
-            finalpad += ' ';
-        }
-    } else {
-        finalpad = pad;
-    }
-    var output = text.replace(/^/gm, finalpad);
-    return output;
-}
-
-proto.filters.truncate = function(text, args) {
-    var len = args[0];
-    if (! text) return null;
-    if (! len)
-        len = 32;
-    // This should probably be <=, but TT just uses <
-    if (text.length < len)
-        return text;
-    var newlen = len - 3;
-    return text.substr(0,newlen) + '...';
-}
-
-proto.filters.repeat = function(text, iter) {
-    if (! text) return null;
-    if (! iter || iter == 0)
-        iter = 1;
-    if (iter == 1) return text
-
-    var output = text;
-    for (var i = 1; i < iter; i++) {
-        output += text;
-    }
-    return output;
-}
-
-proto.filters.replace = function(text, args) {
-    if (! text) return null;
-    var re_search = args[0];
-    var text_replace = args[1];
-    if (! re_search)
-        re_search = '';
-    if (! text_replace)
-        text_replace = '';
-    var re = new RegExp(re_search, 'g');
-    return text.replace(re, text_replace);
-}
-
-//------------------------------------------------------------------------------
-// Jemplate.Stash class
-//------------------------------------------------------------------------------
-if (typeof Jemplate.Stash == 'undefined') {
-    Jemplate.Stash = function() {
-        this.data = {};
-    };
-}
-
-proto = Jemplate.Stash.prototype;
-
-proto.clone = function(args) {
-    var data = this.data;
-    this.data = {};
-    this.update(data);
-    this.update(args);
-    this.data._PARENT = data;
-}
-
-proto.declone = function(args) {
-    this.data = this.data._PARENT || this.data;
-}
-
-proto.update = function(args) {
-    if (typeof args == 'undefined') return;
-    for (var key in args) {
-        var value = args[key];
-        this.set(key, value);
-    }
-}
-
-proto.get = function(key) {
-    var root = this.data;
-    if (key instanceof Array) {
-        for (var i = 0; i < key.length; i += 2) {
-            var args = key.slice(i, i+2);
-            args.unshift(root);
-            value = this._dotop.apply(this, args);
-            if (typeof value == 'undefined')
-                break;
-            root = value;
-        }
-    }
-    else {
-        value = this._dotop(root, key);
-    }
-
-    if (typeof value == 'undefined') {
-        if (this.__config__.DEBUG_UNDEF)
-            throw("undefined value found while using DEGUG_UNDEF");
-        value = '';
-    }
-
-    return value;
-}
-
-proto.set = function(key, value, set_default) {
-    if (key instanceof Array) {
-        var data = this.get(key[0]) || {};
-        key = key[2];
-    }
-    else {
-        data = this.data;
-    }
-    if (! (set_default && (typeof data[key] != 'undefined')))
-        data[key] = value;
-}
-
-proto._dotop = function(root, item, args) {
-    if (typeof item == 'undefined' ||
-        typeof item == 'string' && item.match(/^[\._]/)) {
-        return undefined;
-    }
-
-    if ((! args) &&
-        (typeof root == 'object') &&
-        (!(root instanceof Array) || (typeof item == 'number')) &&
-        (typeof root[item] != 'undefined')) {
-        var value = root[item];
-        if (typeof value == 'function')
-            value = value.apply(root);
-        return value;
-    }
-
-    if (typeof root == 'string' && this.string_functions[item])
-        return this.string_functions[item](root, args);
-    if (root instanceof Array && this.list_functions[item])
-        return this.list_functions[item](root, args);
-    if (typeof root == 'object' && this.hash_functions[item])
-        return this.hash_functions[item](root, args);
-    if (typeof root[item] == 'function')
-        return root[item].apply(root, args);
-
-    return undefined;
-}
-
-proto.string_functions = {};
-
-// chunk(size)     negative size chunks from end
-proto.string_functions.chunk = function(string, args) {
-    var size = args[0];
-    var list = new Array();
-    if (! size)
-        size = 1;
-    if (size < 0) {
-        size = 0 - size;
-        for (i = string.length - size; i >= 0; i = i - size)
-            list.unshift(string.substr(i, size));
-        if (string.length % size)
-            list.unshift(string.substr(0, string.length % size));
-    }
-    else
-        for (i = 0; i < string.length; i = i + size)
-            list.push(string.substr(i, size));
-    return list;
-}
-
-// defined         is value defined?
-proto.string_functions.defined = function(string) {
-    return 1;
-}
-
-// hash            treat as single-element hash with key value
-proto.string_functions.hash = function(string) {
-    return { 'value': string };
-}
-
-// length          length of string representation
-proto.string_functions.length = function(string) {
-    return string.length;
-}
-
-// list            treat as single-item list
-proto.string_functions.list = function(string) {
-    return [ string ];
-}
-
-// match(re)       get list of matches
-proto.string_functions.match = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var list = string.match(regexp);
-    return list;
-}
-
-// repeat(n)       repeated n times
-proto.string_functions.repeat = function(string, args) {
-    var n = args[0] || 1;
-    var output = '';
-    for (var i = 0; i < n; i++) {
-        output += string;
-    }
-    return output;
-}
-
-// replace(re, sub)    replace instances of re with sub
-proto.string_functions.replace = function(string, args) {
-    var regexp = new RegExp(args[0], 'gm');
-    var sub = args[1];
-    if (! sub)
-        sub  = '';
-    var output = string.replace(regexp, sub);
-    return output;
-}
-
-// search(re)      true if value matches re
-proto.string_functions.search = function(string, args) {
-    var regexp = new RegExp(args[0]);
-    return (string.search(regexp) >= 0) ? 1 : 0;
-}
-
-// size            returns 1, as if a single-item list
-proto.string_functions.size = function(string) {
-    return 1;
-}
-
-// split(re)       split string on re
-proto.string_functions.split = function(string, args) {
-    var regexp = new RegExp(args[0]);
-    var list = string.split(regexp);
-    return list;
-}
-
-
-
-proto.list_functions = {};
-
-proto.list_functions.join = function(list, args) {
-    return list.join(args[0]);
-};
-
-proto.list_functions.sort = function(list,key) {
-    if( typeof(key) != 'undefined' && key != "" ) {
-        // we probably have a list of hashes
-        // and need to sort based on hash key
-        return list.sort(
-            function(a,b) {
-                if( a[key] == b[key] ) {
-                    return 0;
-                }
-                else if( a[key] > b[key] ) {
-                    return 1;
-                }
-                else {
-                    return -1;
-                }
-            }
-        );
-    }
-    return list.sort();
-}
-
-proto.list_functions.nsort = function(list) {
-    return list.sort(function(a, b) { return (a-b) });
-}
-
-proto.list_functions.grep = function(list, args) {
-    var regexp = new RegExp(args[0]);
-    var result = [];
-    for (var i = 0; i < list.length; i++) {
-        if (list[i].match(regexp))
-            result.push(list[i]);
-    }
-    return result;
-}
-
-proto.list_functions.unique = function(list) {
-    var result = [];
-    var seen = {};
-    for (var i = 0; i < list.length; i++) {
-        var elem = list[i];
-        if (! seen[elem])
-            result.push(elem);
-        seen[elem] = true;
-    }
-    return result;
-}
-
-proto.list_functions.reverse = function(list) {
-    var result = [];
-    for (var i = list.length - 1; i >= 0; i--) {
-        result.push(list[i]);
-    }
-    return result;
-}
-
-proto.list_functions.merge = function(list, args) {
-    var result = [];
-    var push_all = function(elem) {
-        if (elem instanceof Array) {
-            for (var j = 0; j < elem.length; j++) {
-                result.push(elem[j]);
-            }
-        }
-        else {
-            result.push(elem);
-        }
-    }
-    push_all(list);
-    for (var i = 0; i < args.length; i++) {
-        push_all(args[i]);
-    }
-    return result;
-}
-
-proto.list_functions.slice = function(list, args) {
-    return list.slice(args[0], args[1]);
-}
-
-proto.list_functions.splice = function(list, args) {
-    if (args.length == 1)
-        return list.splice(args[0]);
-    if (args.length == 2)
-        return list.splice(args[0], args[1]);
-    if (args.length == 3)
-        return list.splice(args[0], args[1], args[2]);
-    return null;
-}
-
-proto.list_functions.push = function(list, args) {
-    list.push(args[0]);
-    return list;
-}
-
-proto.list_functions.pop = function(list) {
-    return list.pop();
-}
-
-proto.list_functions.unshift = function(list, args) {
-    list.unshift(args[0]);
-    return list;
-}
-
-proto.list_functions.shift = function(list) {
-    return list.shift();
-}
-
-proto.list_functions.first = function(list) {
-    return list[0];
-}
-
-proto.list_functions.size = function(list) {
-    return list.length;
-}
-
-proto.list_functions.max = function(list) {
-    return list.length - 1;
-}
-
-proto.list_functions.last = function(list) {
-    return list.slice(-1);
-}
-
-proto.hash_functions = {};
-
-
-// each            list of alternating keys/values
-proto.hash_functions.each = function(hash) {
-    var list = new Array();
-    for ( var key in hash )
-        list.push(key, hash[key]);
-    return list;
-}
-
-// exists(key)     does key exist?
-proto.hash_functions.exists = function(hash, args) {
-    return ( typeof( hash[args[0]] ) == "undefined" ) ? 0 : 1;
-}
-
-// FIXME proto.hash_functions.import blows everything up
-//
-// import(hash2)   import contents of hash2
-// import          import into current namespace hash
-//proto.hash_functions.import = function(hash, args) {
-//    var hash2 = args[0];
-//    for ( var key in hash2 )
-//        hash[key] = hash2[key];
-//    return '';
-//}
-
-// keys            list of keys
-proto.hash_functions.keys = function(hash) {
-    var list = new Array();
-    for ( var key in hash )
-        list.push(key);
-    return list;
-}
-
-// list            returns alternating key, value
-proto.hash_functions.list = function(hash, args) {
-    var what = '';
-    if ( args )
-        what = args[0];
-
-    var list = new Array();
-    var key;
-    if (what == 'keys')
-        for ( key in hash )
-            list.push(key);
-    else if (what == 'values')
-        for ( key in hash )
-            list.push(hash[key]);
-    else if (what == 'each')
-        for ( key in hash )
-            list.push(key, hash[key]);
-    else
-        for ( key in hash )
-            list.push({ 'key': key, 'value': hash[key] });
-
-    return list;
-}
-
-// nsort           keys sorted numerically
-proto.hash_functions.nsort = function(hash) {
-    var list = new Array();
-    for (var key in hash)
-        list.push(key);
-    return list.sort(function(a, b) { return (a-b) });
-}
-
-// size            number of pairs
-proto.hash_functions.size = function(hash) {
-    var size = 0;
-    for (var key in hash)
-        size++;
-    return size;
-}
-
-
-// sort            keys sorted alphabetically
-proto.hash_functions.sort = function(hash) {
-    var list = new Array();
-    for (var key in hash)
-        list.push(key);
-    return list.sort();
-}
-
-// values          list of values
-proto.hash_functions.values = function(hash) {
-    var list = new Array();
-    for ( var key in hash )
-        list.push(hash[key]);
-    return list;
-}
-
-//  delete
-proto.hash_functions.remove = function(hash, args) {
-    return delete hash[args[0]];
-}
-proto.hash_functions['delete'] = proto.hash_functions.remove;
-
-//------------------------------------------------------------------------------
-// Jemplate.Iterator class
-//------------------------------------------------------------------------------
-if (typeof Jemplate.Iterator == 'undefined') {
-    Jemplate.Iterator = function(object) {
-        if( object instanceof Array ) {
-            this.object = object;
-            this.size = object.length;
-            this.max  = this.size -1;
-        }
-        else if ( object instanceof Object ) {
-            this.object = object;
-            var object_keys = new Array;
-            for( var key in object ) {
-                object_keys[object_keys.length] = key;
-            }
-            this.object_keys = object_keys.sort();
-            this.size = object_keys.length;
-            this.max  = this.size -1;
-        }
-    }
-}
-
-proto = Jemplate.Iterator.prototype;
-
-proto.get_first = function() {
-    this.index = 0;
-    this.first = 1;
-    this.last  = 0;
-    this.count = 1;
-    return this.get_next(1);
-}
-
-proto.get_next = function(should_init) {
-    var object = this.object;
-    var index;
-    if( typeof(should_init) != 'undefined' && should_init ) {
-        index = this.index;
-    } else {
-        index = ++this.index;
-        this.first = 0;
-        this.count = this.index + 1;
-        if( this.index == this.size -1 ) {
-            this.last = 1;
-        }
-    }
-    if (typeof object == 'undefined')
-        throw('No object to iterate');
-    if( this.object_keys ) {
-        if (index < this.object_keys.length) {
-            this.prev = index > 0 ? this.object_keys[index - 1] : "";
-            this.next = index < this.max ? this.object_keys[index + 1] : "";
-            return [this.object_keys[index], false];
-        }
-    } else {
-        if (index < object.length) {
-            this.prev = index > 0 ? object[index - 1] : "";
-            this.next = index < this.max ? object[index +1] : "";
-            return [object[index], false];
-        }
-    }
-    return [null, true];
-}
-
-var stubExplanation = "stub that doesn't do anything. Try including the jQuery, YUI, or XHR option when building the runtime";
-
-Jemplate.Ajax = {
-
-    get: function(url, callback) {
-        throw("This is a Jemplate.Ajax.get " + stubExplanation);
-    },
-
-    processGet: function(url, callback) {
-        throw("This is a Jemplate.Ajax.processGet " + stubExplanation);
-    },
-
-    post: function(url, callback) {
-        throw("This is a Jemplate.Ajax.post " + stubExplanation);
-    }
-
-};
-
-Jemplate.JSON = {
-
-    parse: function(decodeValue) {
-        throw("This is a Jemplate.JSON.parse " + stubExplanation);
-    },
-
-    stringify: function(encodeValue) {
-        throw("This is a Jemplate.JSON.stringify " + stubExplanation);
-    }
-
-};
-
-}());
-
-;;(function(){
-
-Jemplate.Ajax = {
-
-    get: function(url, callback) {
-        if (typeof callback == "function") {
-            callback = { success: callback };
-        }
-        YAHOO.connect.asyncRequest("GET", url, callback);
-    },
-
-    processGet: function(url, processor) {
-        this.get(url, function(responseText){
-            process(YAHOO.lang.JSON.parse(responseText));
-        });
-    },
-
-    post: function(url, data, callback) {
-        if (typeof callback == "function") {
-            callback = { success: callback };
-        }
-        YAHOO.connect.asyncRequest("POST", url, callback, data);
-    }
-
-};
-
-}());
-
-;;(function(){
-
-Jemplate.JSON = {
-
-    parse: function(encoded) {
-        return YAHOO.lang.JSON.parse(encoded);
-    },
-
-    stringify: function(decoded) {
-        return YAHOO.lang.JSON.stringify(decoded);
-    }
-
-};
-
-}());
-
-1
\ No newline at end of file
@@ -1,36 +0,0 @@
-.PHONY: all clean jemplate
-
-ALL_T_HTML:=$(shell ls -1 ../*.t.js | perl -pe 's/js$$/html/;s/^\.\.\///')
-
-ALL_TARGETS:=$(ALL_T_HTML) index.html JemplateRuntime.js jemplate.js jemplate2.js
-
-all: $(ALL_TARGETS)
-	ln -fs ../Test .
-	ln -fs ../*.t.js .
-	ln -fs ../jquery*.js .
-
-%.t.html: template/test.html
-	perl bin/render-template $< $@
-
-index.html: template/index.html ../*.t.js
-	perl bin/render-template $< $@
-
-JemplateRuntime.js:
-	perl -I../../lib ../../jemplate --runtime=yui > $@
-#	perl -I../../lib ../../jemplate --runtime > $@
-
-jemplate.js: jemplate ../*.t.js
-	perl -I../../lib ../../jemplate --compile jemplate/* > $@
-
-jemplate2.js: jemplate2
-	perl -I../../lib ../../jemplate --compile jemplate2 \
-		--start-tag='<!' --end-tag='!>' --any-case --post-chomp > $@
-
-jemplate: ../*.t.js
-	perl bin/pull-jemplates
-
-clean:
-	rm -fr $(ALL_TARGETS) jemplate/* *.t.js Test
-
-html:
-	mkdir html
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../basics.t.js';
-    </script>
-    <script type="text/javascript" src="../basics.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,108 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-use Getopt::Long    qw[];
-use HTTP::Daemon    qw[];
-use HTTP::Response  qw[];
-use HTTP::Status    qw[RC_FORBIDDEN];
-use LWP::MediaTypes qw[guess_media_type];
-use Path::Class     qw[];
-use Pod::Usage      qw[];
-
-$SIG{'PIPE'} = 'IGNORE';
-
-Getopt::Long::Configure( 'no_ignore_case' );
-Getopt::Long::Configure( 'bundling' );
-Getopt::Long::GetOptions(
-    'p|port=i' => \( my $port = 8080 ),
-    'H|host=s' => \( my $host ),
-    'h|help|?' => sub { Pod::Usage::pod2usage( { -verbose => 1, -exitval => 0 } ) },
-    'm|man'    => sub { Pod::Usage::pod2usage( { -verbose => 2, -exitval => 0 } ) },
-) or exit 1;
-
-my $base = Path::Class::File->new($0)->parent->parent->absolute;
-my %args = ( LocalPort => $port, ReuseAddr => 1 );
-
-if ( defined $host ) {
-    $args{LocalHost} = $host;
-}
-
-my $daemon = HTTP::Daemon->new(%args)
-  or die("Failed to create daemon: $!\n");
-
-print "Please contact me at: <URL:", $daemon->url, ">\n";
-
-while ( my $connection = $daemon->accept ) {
-
-    while ( my $request = $connection->get_request ) {
-
-        my $file = $request->url->path =~ m|^/share|
-          ? $base->parent->file( $request->url->path )
-          : $request->url->path eq '/'
-              ? $base->file('index.html')
-              : $base->file( $request->url->path );
-
-        $file = $file->cleanup->absolute;
-
-        if ( -e $file && $request->method eq 'GET' ) {
-
-            print "$file\n";
-
-            my $stat     = $file->stat;
-            my $type     = ( guess_media_type( $file->stringify ) )[0];
-            my $response = HTTP::Response->new( 200, 'OK' );
-            $response->header( 'Connection'    => 'close' );
-            $response->header( 'Cache-Control' => 'no-cache, max-age=0' );
-
-            if ( 0 && $request->if_modified_since || 0 == $stat->mtime ) {
-                $response->code(304);
-                $response->message('Not Modified');
-            }
-            else {
-                $response->content( scalar $file->slurp );
-                $response->content_length( $stat->size );
-                $response->content_type($type);
-                $response->last_modified( $stat->mtime );
-            }
-
-            $connection->send_response($response);
-        }
-        else {
-
-            print "$file FORBIDDEN\n";
-
-            $connection->send_error(RC_FORBIDDEN);
-            $connection->close;
-        }
-    }
-}
-
-__END__
-
-=head1 NAME
-
-daemon - Jemplate test daemon
-
-=head1 SYNOPSIS
-
-daemon [-p]
-
- Options:
-   -H --host   Host to listen to
-   -p --port   Port to listen to
-   -m --man    Displays manpage
-   -h --help   Displays this help
-
-=head1 COMMAND LINE OPTIONS
-
-=head2 --host
-
-Host to listen to. Defaults to listen to the given port on all interfaces.
-
-=head2 --port
-
-Port to listen to. Defaults to listen to 8080.
-
-=cut
@@ -1,25 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use IO::All;
-
-my $output_dir = 'jemplate';
-
-my %jemplates;
-
-for my $test (glob '../*.t.js') {
-    my $file_contents = io($test)->all;
-    my (@jemplates) = ($file_contents =~ /(^--- jemplate\n.*?)(?=^---|\z)/gsm);
-    for my $jemplate (@jemplates) {
-        my ($ignore, $jemplate_file, $jemplate_content) =
-            split /\n/, $jemplate, 3;
-        $jemplates{$jemplate_file} = $jemplate_content;
-    }
-}
-
-mkdir $output_dir unless -d $output_dir;
-for my $jemplate_file (sort keys %jemplates) {
-    io("$output_dir/$jemplate_file")->print($jemplates{$jemplate_file});
-}
-
@@ -1,32 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use Template;
-use IO::All;
-use YAML;
-
-my ($template_path, $output_path) = @ARGV;
-
-my $test_file_name = $output_path;
-$test_file_name =~ s/^/..\// or die;
-$test_file_name =~ s/\.html$/.js/ or die;
-
-my $t = Template->new(
-    {
-        INCLUDE_PATH => ['.', 'template/'],
-    }
-);
-
-my $config = YAML::LoadFile('../config.yaml');
-my $data = {
-    %$config,
-    test_file => $test_file_name,
-    all_test_files => [ glob('*.t.html') ],
-#    all_test_files => [ grep { !m/plugins/ } glob('*.t.html') ]
-};
-my $result;
-
-$t->process($template_path, $data, \$result) or die $t->error;
-
-io($output_path)->print($result);
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../bugs.t.js';
-    </script>
-    <script type="text/javascript" src="../bugs.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../default.t.js';
-    </script>
-    <script type="text/javascript" src="../default.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../directives.t.js';
-    </script>
-    <script type="text/javascript" src="../directives.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../filters.t.js';
-    </script>
-    <script type="text/javascript" src="../filters.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../greetings.t.js';
-    </script>
-    <script type="text/javascript" src="../greetings.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../hash.t.js';
-    </script>
-    <script type="text/javascript" src="../hash.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../hello.t.js';
-    </script>
-    <script type="text/javascript" src="../hello.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,32 +0,0 @@
-<html>
-<head>
-    <title>Jemplate Testing</title>
-    <script type="text/javascript" src="lib/Test/Harness.js"></script>
-    <script type="text/javascript" src="lib/Test/Harness/Browser.js"></script>
-</head>
-<body>
-<script type="text/javascript">
-    var t = new Test.Harness.Browser();
-    
-    t.runTests(
-        "basics.t.html",
-        "bugs.t.html",
-        "default.t.html",
-        "directives.t.html",
-        "filters.t.html",
-        "greetings.t.html",
-        "hash.t.html",
-        "hello.t.html",
-        "join.t.html",
-        "list.t.html",
-        "localise.t.html",
-        "loop.t.html",
-        "objects.t.html",
-        "plugins.t.html",
-        "set.t.html",
-        "stash.t.html",
-        "string.t.html"
-    );
-</script>
-</body>
-</html>
@@ -1,10 +0,0 @@
-[%- JAVASCRIPT -%]
-stash.set( "simple_list", ["a","b","c"] );
-stash.set( "mylist", [["a","b","c"],["d","e","f"],["h","i","j"]] );
-[%- END -%]
-a = [% simple_list.0 %]
-a = [% mylist.0.0 %]
-c = [% mylist.0.2 %]
-e = [% mylist.1.1 %]
-j = [% mylist.2.2 -%]
-
@@ -1,3 +0,0 @@
-[% DEFAULT foo = 'two', bar = 'three' -%]
-[% DEFAULT bar = 'four' -%]
-[% foo %] | [% bar %]
@@ -1,7 +0,0 @@
-[% BLOCK foo -%]
-I <3 Sushi
-[% END -%]
-[% SET list = [3, 4, 5] -%]
-[% FOR i = list -%]
-[% PROCESS foo -%]
-[% END -%]
@@ -1,7 +0,0 @@
-[% SET num = 4 -%]
-[% array = [] -%]
-[% WHILE num < 7 -%]
-[% IF num % 2 %][% CALL array.push('Odd') %][% ELSE %][% CALL array.push('Even') %][% END -%]
-[% num = num + 1 -%]
-[% END -%]
-[% array.join('***') %]
@@ -1,5 +0,0 @@
-[%- JAVASCRIPT -%]
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-[%- END -%]
-Key1: [% obj.key1 %]
-Key2: [% obj.key2 %]
@@ -1,6 +0,0 @@
-[%- JAVASCRIPT -%]
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-[%- END -%]
-[%- FOR key IN obj -%]
-[% key %]: [% obj.$key %]
-[% END -%]
@@ -1,4 +0,0 @@
-[%- FOR i IN [1,2,3,4,5,6] -%]
-[%- NEXT IF i % 2 == 0 -%]
-I = [% i %];
-[%- END -%]
@@ -1,7 +0,0 @@
-[% FILTER html %]This is some html text
-All the <tags> should be escaped & protected
-[% END %]
-[% text = "The <cat> sat on the <mat>" %]
-[% text FILTER html %]
-[% FILTER html %]
-"It isn't what I expected", he replied.[% END %]
@@ -1,30 +0,0 @@
-[% FILTER indent -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent(3) -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent('2') -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent(0) -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% text = 'The cat sat on the mat';
-   text | indent('> ') | indent('+') %]
@@ -1 +0,0 @@
-[%- "Ils ont les chapeaux ronds, vive la bretagne" | null -%]
@@ -1 +0,0 @@
-[% "foo..." FILTER repeat(5) %]
@@ -1,6 +0,0 @@
-[%- text = "The cat sat on the mat" %]
-[%- text FILTER replace(' ', '_') %]
-[% text FILTER replace('sat', 'shat') +%]
-[% text | replace('at', 'plat') +%]
-[% text = 'The <=> operator, blah, blah' %]
-[%- text | html | replace('blah', 'rhubarb') %]
@@ -1,7 +0,0 @@
-[% a = '1234567890' -%]
-[% a | truncate(5)  %]
-[% a | truncate(10) %]
-[% a | truncate(15) %]
-[% a | truncate(2)  %]
-[% a = '1234567890123456789012345678901234567890' -%]
-[% a | truncate  %]
@@ -1,5 +0,0 @@
-[% "my file.html" FILTER uri %]
-[% "my<file & your>file.html" FILTER uri %]
-[% "my<file & your>file.html" | uri | html %]
-[% "guitar&amp;file.html" | uri %]
-[% "guitar&amp;file.html" | uri | html %]
@@ -1,11 +0,0 @@
-[%# This is a bit convoluted because sort order can not be guaranteed -%]
-[%# please fix it if you can find a better way... -%]
-[%# There are a few other tests below that are very similar -%]
-[% SET hash = { a=1 b=2 c=3 };
-   SET list = hash.each;
-   FOR kindex IN [0,2,4];
-     SET vindex = kindex + 1;
-     SET key = list.${kindex};
-     SET value = list.${vindex};
-     (key AND hash.${key} == value) ? 1 : 0;
-   END %]
@@ -1,3 +0,0 @@
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.exists('b') ? 1 : 0 %]
-[% a.exists('z') ? 1 : 0 %]
@@ -1,6 +0,0 @@
-[%# SET a = { a=1 b=2 c=3 } -%]
-[%# CALL a.import({ a=2 b=1 d=4 e=5 }) -%]
-[%# a.exists('c') ? 1 : 0 %]1
-[%# a.exists('d') ? 1 : 0 %]1
-[%# a.a %]2
-[%# a.e %]5
@@ -1,2 +0,0 @@
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.keys.sort.join(' ') %]
@@ -1,16 +0,0 @@
-[% SET hash = { a=1 b=2 c=3 } -%]
-[% hash.list('keys').sort.join(' ') %]
-[% hash.list('values').sort.join(' ') %]
-[% SET list = hash.list('each');
-   FOR kindex IN [0,2,4];
-     SET vindex = kindex + 1;
-     SET key = list.${kindex};
-     SET value = list.${vindex};
-     (key AND hash.${key} == value) ? 1 : 0;
-   END %]
-[% SET list = hash.list();
-   FOR entry IN list;
-     SET key = entry.key;
-     SET value = entry.value;
-     (hash.${key} == value) ? 1 : 0;
-   END %]
@@ -1,2 +0,0 @@
-[% SET a = { '499'='c' '5'='a' '50'='b' } -%]
-[% a.nsort.join(' ') %]
@@ -1,2 +0,0 @@
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.size %]
@@ -1,2 +0,0 @@
-[% SET a = { ac=1 b=2 aa=3 } -%]
-[% a.sort.join(' ') %]
@@ -1,2 +0,0 @@
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.values.nsort.join(' ') %]
@@ -1 +0,0 @@
-Hello, [% name %]
@@ -1,2 +0,0 @@
-[% a = ['foo', 'bar', 'baz'] %]
-[%- a.join('::') -%]
@@ -1,18 +0,0 @@
-[% SET a1 = [ 'one', 'two', 'three'] -%]
-[% CALL a1.push('four') -%]
-[%- a1.first() %] - [% a1.last() %]
-[% a1.grep('o').join('/') %]
-[% a1.max() %]+[% a1.size() %]
-[% SET a2 = a1.reverse -%]
-[% a2.join('^') %]
-[% a2.slice(1, 3).join('*') %]
-[% SET a3 = [ 5, 9, 'x', 17, 9, 33, 12, 'x', 5] -%]
-[% a3.unique().join(',') %]
-[% a1.unshift('zero').sort().join('!') %]
-[% CALL a1.shift(); CALL a1.pop(); a1.join('_') %]
-[% CALL a3.splice(2,1); CALL a3.splice(-2,1); a3.nsort.join('~') %]
-[% SET a4 = [11, 22, 33] -%]
-[% SET a5 = [44, 55, 66] -%]
-[% SET a6 = [77, 88, 99] -%]
-[% SET a7 = a4.merge(a5, 'foo', a6) -%]
-[% a7.join("'") %]
@@ -1,4 +0,0 @@
-[% SET ll = [ {'a' => 9}, {'a' =>  1}, {'a' => 3} ] -%]
-[%- FOR hash = ll.sort('a') -%]
-[%- hash.a -%]:
-[%- END -%]
@@ -1,8 +0,0 @@
-[% thing %]
-[% SET thing = 'bar' -%]
-[% thing %]
-[% INCLUDE myblock -%]
-[% thing %]
-[% BLOCK myblock -%]
-[% SET thing = 'baz' -%]
-[% END -%]
@@ -1,8 +0,0 @@
-[% thing %]
-[% SET thing = 'bar' -%]
-[% thing %]
-[% PROCESS myblock -%]
-[% thing %]
-[% BLOCK myblock -%]
-[% SET thing = 'baz' -%]
-[% END -%]
@@ -1,3 +0,0 @@
-[%- FOR i IN [0,1,2,3] -%]
-[%- i -%]:[%- loop.index %]
-[% END -%]
@@ -1,9 +0,0 @@
-[%- FOR i IN [0,1,2,3] -%]
-    [%- IF loop.first -%]
-[start]
-    [%- END -%]
-    [%- i -%]
-    [%- IF loop.last -%]
-[end]
-    [%- END -%]
-[%- END %]
@@ -1,3 +0,0 @@
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.count%]
-[% END -%]
@@ -1,3 +0,0 @@
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.size%]:[%-loop.max%]
-[% END -%]
@@ -1,6 +0,0 @@
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
-[% END -%]
-[%- FOR i IN {a => 0, b => 1, c => 3, d => 4 } -%]
-    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
-[% END -%]
@@ -1,4 +0,0 @@
-[%- JAVASCRIPT -%]
-stash.set( "object", { name: 'Wally' } );
-[%- END -%]
-Hello, [% object.name %]
@@ -1,5 +0,0 @@
-[%- JAVASCRIPT -%]
-var obj = new TheName('Bally');
-stash.set( "objectName", obj.getName() );
-[%- END -%]
-Hello, [% objectName %]
@@ -1,4 +0,0 @@
-[%- JAVASCRIPT -%]
-stash.set( "object", new TheName('Mally') );
-[%- END -%]
-Hello, [% object.getName() %]
@@ -1,4 +0,0 @@
-[%- JAVASCRIPT -%]
-stash.set( "object", new TheName('Larry') );
-[%- END -%]
-Hello, [% object.name %]
@@ -1,3 +0,0 @@
-[%- IF "abc" == "abc" -%]
-same
-[%- END %]
@@ -1,4 +0,0 @@
-[%- IF "abc" != "def" -%]
-not same
-[%- END -%]
-
@@ -1,2 +0,0 @@
-[%- "abc" _ "def" -%]
-
@@ -1,9 +0,0 @@
-[% USE dummy('simple param') -%]
-[% dummy.simple %]
-#
-[% dummy.params('one', 'two') %]
-#
-[% dummy.what %]
-#
-[% dummy.get_what %]
-#
@@ -1,2 +0,0 @@
-[% hash.keys.sort.join('+') %]
-[% hash.keys().sort().join('+') %]
@@ -1,2 +0,0 @@
-[% hash.keys %]
-[% hash.keys().join('+') %]
@@ -1,3 +0,0 @@
-[% hash.noarg %]
-[% hash.noarg() %]
-[% hash.arg("abc") %]
@@ -1,11 +0,0 @@
-[% SET a = '1234567890' -%]
-[% a.chunk.join(' ') %]
-[% a.chunk(2).join(' ') %]
-[% a.chunk(3).join(' ') %]
-[% a.chunk(9).join(' ') %]
-[% a.chunk(10).join(' ') %]
-[% a.chunk(-1).join(' ') %]
-[% a.chunk(-2).join(' ') %]
-[% a.chunk(-3).join(' ') %]
-[% a.chunk(-9).join(' ') %]
-[% a.chunk(-10).join(' ') %]
@@ -1,2 +0,0 @@
-[% SET a = '1' -%]
-[% a.defined ? '1' : '0' %]
@@ -1,2 +0,0 @@
-[% SET a = 'Hi' -%]
-[% a.hash.value %]
@@ -1,4 +0,0 @@
-[% SET a = 'Hi' -%]
-[% a.length %]
-[% SET a = 10 -%]
-[% a.length %]
@@ -1,2 +0,0 @@
-[% SET a = 'Hi' -%]
-[% a.list.0 %]
@@ -1,2 +0,0 @@
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.match('\\\d\\\d').join(' ') %]
@@ -1,3 +0,0 @@
-[% SET a = 'aaa' -%]
-[% a.repeat(3) %]
-[% a.repeat() %]
@@ -1,3 +0,0 @@
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.replace('\\\d\\\d', 'bb') %]
-[% a.replace('\\\d\\\d') %]
@@ -1,3 +0,0 @@
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.search('\\\d\\\d') ? 1 : 0 %]
-[% a.search('w') ? 1 : 0 %]
@@ -1,2 +0,0 @@
-[% SET a = '1' -%]
-[% a.defined ? '1' : '0' %]
@@ -1,4 +0,0 @@
-[% SET a = 'aaa12aaa34aaa' -%]
-[% a.split('\\\d\\\d').join(' ') %]
-[% SET a = '1aaa2aaa3aaa4' -%]
-[% a.split('aaa').join(' ') %]
@@ -1,4 +0,0 @@
-[% testhash = {} -%]
-[% testkey = 'bob' -%]
-[% testhash.$testkey = 'bozo' -%]
-Value: [% testhash.$testkey %]<br />
@@ -1,2101 +0,0 @@
-/*
-   This JavaScript code was generated by Jemplate, the JavaScript
-   Template Toolkit. Any changes made to this file will be lost the next
-   time the templates are compiled.
-
-   Copyright 2006 - Ingy döt Net - All rights reserved.
-*/
-
-if (typeof(Jemplate) == 'undefined')
-    throw('Jemplate.js must be loaded before any Jemplate template files');
-
-Jemplate.templateMap['basic_array1.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 4 "basic_array1.html"
-stash.set( "simple_list", ["a","b","c"] );
-stash.set( "mylist", [["a","b","c"],["d","e","f"],["h","i","j"]] );
-output += 'a = ';
-//line 5 "basic_array1.html"
-output += stash.get(['simple_list', 0, 0, 0]);
-output += '\na = ';
-//line 6 "basic_array1.html"
-output += stash.get(['mylist', 0, 0, 0, 0, 0]);
-output += '\nc = ';
-//line 7 "basic_array1.html"
-output += stash.get(['mylist', 0, 0, 0, 2, 0]);
-output += '\ne = ';
-//line 8 "basic_array1.html"
-output += stash.get(['mylist', 0, 1, 0, 1, 0]);
-output += '\nj = ';
-//line 9 "basic_array1.html"
-output += stash.get(['mylist', 0, 2, 0, 2, 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['default.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "default.html"
-stash.set('foo', 'two', 1);
-stash.set('bar', 'three', 1);
-//line 2 "default.html"
-stash.set('bar', 'four', 1);
-//line 3 "default.html"
-output += stash.get('foo');
-output += ' | ';
-//line 3 "default.html"
-output += stash.get('bar');
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['directives1.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-
-//line 4 "directives1.html"
-stash.set('list', [ 3, 4, 5 ]);
-//line 7 "directives1.html"
-
-// FOREACH 
-(function() {
-    var list = stash.get('list');
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 6 "directives1.html"
-output += context.process('foo');;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['foo'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += 'I <3 Sushi\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['directives2.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "directives2.html"
-stash.set('num', 4);
-//line 2 "directives2.html"
-stash.set('array', [ ]);
-//line 6 "directives2.html"
-    
-// WHILE
-var failsafe = 1000;
-while (--failsafe && (stash.get('num') < 7)) {
-//line 4 "directives2.html"
-if (stash.get('num') % 2) {
-//line 4 "directives2.html"
-stash.get(['array', 0, 'push', [ 'Odd' ]]);
-}
-else {
-//line 4 "directives2.html"
-stash.get(['array', 0, 'push', [ 'Even' ]]);
-}
-
-//line 5 "directives2.html"
-stash.set('num', stash.get('num') + 1);
-}
-if (! failsafe)
-    throw("WHILE loop terminated (> 1000 iterations)\n")
-
-//line 7 "directives2.html"
-output += stash.get(['array', 0, 'join', [ '***' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['directives3.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "directives3.html"
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-output += 'Key1: ';
-//line 4 "directives3.html"
-output += stash.get(['obj', 0, 'key1', 0]);
-output += '\nKey2: ';
-//line 5 "directives3.html"
-output += stash.get(['obj', 0, 'key2', 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['directives4.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "directives4.html"
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-//line 6 "directives4.html"
-
-// FOREACH 
-(function() {
-    var list = stash.get('obj');
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['key'] = value;
-//line 5 "directives4.html"
-output += stash.get('key');
-output += ': ';
-//line 5 "directives4.html"
-output += stash.get(['obj', 0, stash.get('key'), 0]);
-output += '\n';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['directives5.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 4 "directives5.html"
-
-// FOREACH 
-(function() {
-    var list = [ 1, 2, 3, 4, 5, 6 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 2 "directives5.html"
-if (stash.get('i') % 2 == 0) {
-  retval = list.get_next();
-  value = retval[0];
-  done = retval[1];
-  continue;
-
-}
-
-output += 'I = ';
-//line 3 "directives5.html"
-output += stash.get('i');
-output += ';';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_html.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "filters_html.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'This is some html text\nAll the <tags> should be escaped & protected\n';
-
-    return context.filter(output, 'html', []);
-})();
-
-output += '\n';
-//line 4 "filters_html.html"
-stash.set('text', 'The <cat> sat on the <mat>');
-output += '\n';
-//line 5 "filters_html.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('text');
-
-    return context.filter(output, 'html', []);
-})();
-
-output += '\n';
-//line 7 "filters_html.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += '\n"It isn\'t what I expected", he replied.';
-
-    return context.filter(output, 'html', []);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_indent.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 6 "filters_indent.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += '1\n2\n3\n4';
-
-    return context.filter(output, 'indent', []);
-})();
-
-output += '\n#\n';
-//line 13 "filters_indent.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += '1\n2\n3\n4';
-
-    return context.filter(output, 'indent', [ 3 ]);
-})();
-
-output += '\n#\n';
-//line 20 "filters_indent.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += '1\n2\n3\n4';
-
-    return context.filter(output, 'indent', [ '2' ]);
-})();
-
-output += '\n#\n';
-//line 27 "filters_indent.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += '1\n2\n3\n4';
-
-    return context.filter(output, 'indent', [ 0 ]);
-})();
-
-output += '\n#\n';
-//line 29 "filters_indent.html"
-stash.set('text', 'The cat sat on the mat');
-//line 29 "filters_indent.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('text');
-
-    return context.filter(output, 'indent', [ '> ' ]);
-})();
-
-
-    return context.filter(output, 'indent', [ '+' ]);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_null.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "filters_null.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'Ils ont les chapeaux ronds, vive la bretagne';
-
-    return context.filter(output, 'null', []);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_repeat.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "filters_repeat.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'foo...';
-
-    return context.filter(output, 'repeat', [ 5 ]);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_replace.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "filters_replace.html"
-stash.set('text', 'The cat sat on the mat');
-//line 2 "filters_replace.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('text');
-
-    return context.filter(output, 'replace', [ ' ', '_' ]);
-})();
-
-output += '\n';
-//line 3 "filters_replace.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('text');
-
-    return context.filter(output, 'replace', [ 'sat', 'shat' ]);
-})();
-
-output += '\n';
-//line 4 "filters_replace.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('text');
-
-    return context.filter(output, 'replace', [ 'at', 'plat' ]);
-})();
-
-output += '\n';
-//line 5 "filters_replace.html"
-stash.set('text', 'The <=> operator, blah, blah');
-//line 6 "filters_replace.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('text');
-
-    return context.filter(output, 'html', []);
-})();
-
-
-    return context.filter(output, 'replace', [ 'blah', 'rhubarb' ]);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_truncate.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "filters_truncate.html"
-stash.set('a', '1234567890');
-//line 2 "filters_truncate.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('a');
-
-    return context.filter(output, 'truncate', [ 5 ]);
-})();
-
-output += '\n';
-//line 3 "filters_truncate.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('a');
-
-    return context.filter(output, 'truncate', [ 10 ]);
-})();
-
-output += '\n';
-//line 4 "filters_truncate.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('a');
-
-    return context.filter(output, 'truncate', [ 15 ]);
-})();
-
-output += '\n';
-//line 5 "filters_truncate.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('a');
-
-    return context.filter(output, 'truncate', [ 2 ]);
-})();
-
-output += '\n';
-//line 6 "filters_truncate.html"
-stash.set('a', '1234567890123456789012345678901234567890');
-//line 7 "filters_truncate.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += stash.get('a');
-
-    return context.filter(output, 'truncate', []);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['filters_uri.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "filters_uri.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'my file.html';
-
-    return context.filter(output, 'uri', []);
-})();
-
-output += '\n';
-//line 2 "filters_uri.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'my<file & your>file.html';
-
-    return context.filter(output, 'uri', []);
-})();
-
-output += '\n';
-//line 3 "filters_uri.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'my<file & your>file.html';
-
-    return context.filter(output, 'uri', []);
-})();
-
-
-    return context.filter(output, 'html', []);
-})();
-
-output += '\n';
-//line 4 "filters_uri.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'guitar&amp;file.html';
-
-    return context.filter(output, 'uri', []);
-})();
-
-output += '\n';
-//line 5 "filters_uri.html"
-
-// FILTER
-output += (function() {
-    var output = '';
-
-
-// FILTER
-output += (function() {
-    var output = '';
-
-output += 'guitar&amp;file.html';
-
-    return context.filter(output, 'uri', []);
-})();
-
-
-    return context.filter(output, 'html', []);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_each.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 4 "hash_each.html"
-stash.set('hash', { 'a': 1, 'b': 2, 'c': 3  });
-//line 4 "hash_each.html"
-stash.set('list', stash.get(['hash', 0, 'each', 0]));
-//line 4 "hash_each.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 2, 4 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['kindex'] = value;
-//line 4 "hash_each.html"
-stash.set('vindex', stash.get('kindex') + 1);
-//line 4 "hash_each.html"
-stash.set('key', stash.get(['list', 0, stash.get('kindex'), 0]));
-//line 4 "hash_each.html"
-stash.set('value', stash.get(['list', 0, stash.get('vindex'), 0]));
-//line 4 "hash_each.html"
-output += (stash.get('key') && stash.get(['hash', 0, stash.get('key'), 0]) == stash.get('value')) ? 1 : 0;;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_exists.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_exists.html"
-stash.set('a', { 'a': 1, 'b': 2, 'c': 3  });
-//line 2 "hash_exists.html"
-output += stash.get(['a', 0, 'exists', [ 'b' ]]) ? 1 : 0;
-output += '\n';
-//line 3 "hash_exists.html"
-output += stash.get(['a', 0, 'exists', [ 'z' ]]) ? 1 : 0;
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_import.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += '1\n';
-output += '1\n';
-output += '2\n';
-output += '5\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_keys.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_keys.html"
-stash.set('a', { 'a': 1, 'b': 2, 'c': 3  });
-//line 2 "hash_keys.html"
-output += stash.get(['a', 0, 'keys', 0, 'sort', 0, 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_list.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_list.html"
-stash.set('hash', { 'a': 1, 'b': 2, 'c': 3  });
-//line 2 "hash_list.html"
-output += stash.get(['hash', 0, 'list', [ 'keys' ], 'sort', 0, 'join', [ ' ' ]]);
-output += '\n';
-//line 3 "hash_list.html"
-output += stash.get(['hash', 0, 'list', [ 'values' ], 'sort', 0, 'join', [ ' ' ]]);
-output += '\n';
-//line 4 "hash_list.html"
-stash.set('list', stash.get(['hash', 0, 'list', [ 'each' ]]));
-//line 4 "hash_list.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 2, 4 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['kindex'] = value;
-//line 4 "hash_list.html"
-stash.set('vindex', stash.get('kindex') + 1);
-//line 4 "hash_list.html"
-stash.set('key', stash.get(['list', 0, stash.get('kindex'), 0]));
-//line 4 "hash_list.html"
-stash.set('value', stash.get(['list', 0, stash.get('vindex'), 0]));
-//line 4 "hash_list.html"
-output += (stash.get('key') && stash.get(['hash', 0, stash.get('key'), 0]) == stash.get('value')) ? 1 : 0;;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-output += '\n';
-//line 11 "hash_list.html"
-stash.set('list', stash.get(['hash', 0, 'list', []]));
-//line 11 "hash_list.html"
-
-// FOREACH 
-(function() {
-    var list = stash.get('list');
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['entry'] = value;
-//line 11 "hash_list.html"
-stash.set('key', stash.get(['entry', 0, 'key', 0]));
-//line 11 "hash_list.html"
-stash.set('value', stash.get(['entry', 0, 'value', 0]));
-//line 11 "hash_list.html"
-output += (stash.get(['hash', 0, stash.get('key'), 0]) == stash.get('value')) ? 1 : 0;;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_nsort.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_nsort.html"
-stash.set('a', { '499': 'c', '5': 'a', '50': 'b'  });
-//line 2 "hash_nsort.html"
-output += stash.get(['a', 0, 'nsort', 0, 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_size.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_size.html"
-stash.set('a', { 'a': 1, 'b': 2, 'c': 3  });
-//line 2 "hash_size.html"
-output += stash.get(['a', 0, 'size', 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_sort.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_sort.html"
-stash.set('a', { 'ac': 1, 'b': 2, 'aa': 3  });
-//line 2 "hash_sort.html"
-output += stash.get(['a', 0, 'sort', 0, 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hash_values.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "hash_values.html"
-stash.set('a', { 'a': 1, 'b': 2, 'c': 3  });
-//line 2 "hash_values.html"
-output += stash.get(['a', 0, 'values', 0, 'nsort', 0, 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hello.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += 'Hello, ';
-//line 1 "hello.html"
-output += stash.get('name');
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['join.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "join.html"
-stash.set('a', [ 'foo', 'bar', 'baz' ]);
-//line 2 "join.html"
-output += stash.get(['a', 0, 'join', [ '::' ]]);
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['list.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "list.html"
-stash.set('a1', [ 'one', 'two', 'three' ]);
-//line 2 "list.html"
-stash.get(['a1', 0, 'push', [ 'four' ]]);
-//line 3 "list.html"
-output += stash.get(['a1', 0, 'first', []]);
-output += ' - ';
-//line 3 "list.html"
-output += stash.get(['a1', 0, 'last', []]);
-output += '\n';
-//line 4 "list.html"
-output += stash.get(['a1', 0, 'grep', [ 'o' ], 'join', [ '/' ]]);
-output += '\n';
-//line 5 "list.html"
-output += stash.get(['a1', 0, 'max', []]);
-output += '+';
-//line 5 "list.html"
-output += stash.get(['a1', 0, 'size', []]);
-output += '\n';
-//line 6 "list.html"
-stash.set('a2', stash.get(['a1', 0, 'reverse', 0]));
-//line 7 "list.html"
-output += stash.get(['a2', 0, 'join', [ '^' ]]);
-output += '\n';
-//line 8 "list.html"
-output += stash.get(['a2', 0, 'slice', [ 1, 3 ], 'join', [ '*' ]]);
-output += '\n';
-//line 9 "list.html"
-stash.set('a3', [ 5, 9, 'x', 17, 9, 33, 12, 'x', 5 ]);
-//line 10 "list.html"
-output += stash.get(['a3', 0, 'unique', [], 'join', [ ',' ]]);
-output += '\n';
-//line 11 "list.html"
-output += stash.get(['a1', 0, 'unshift', [ 'zero' ], 'sort', [], 'join', [ '!' ]]);
-output += '\n';
-//line 12 "list.html"
-stash.get(['a1', 0, 'shift', []]);
-//line 12 "list.html"
-stash.get(['a1', 0, 'pop', []]);
-//line 12 "list.html"
-output += stash.get(['a1', 0, 'join', [ '_' ]]);
-output += '\n';
-//line 13 "list.html"
-stash.get(['a3', 0, 'splice', [ 2, 1 ]]);
-//line 13 "list.html"
-stash.get(['a3', 0, 'splice', [ -2, 1 ]]);
-//line 13 "list.html"
-output += stash.get(['a3', 0, 'nsort', 0, 'join', [ '~' ]]);
-output += '\n';
-//line 14 "list.html"
-stash.set('a4', [ 11, 22, 33 ]);
-//line 15 "list.html"
-stash.set('a5', [ 44, 55, 66 ]);
-//line 16 "list.html"
-stash.set('a6', [ 77, 88, 99 ]);
-//line 17 "list.html"
-stash.set('a7', stash.get(['a4', 0, 'merge', [ stash.get('a5'), 'foo', stash.get('a6') ]]));
-//line 18 "list.html"
-output += stash.get(['a7', 0, 'join', [ '\'' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['list_of_hash.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "list_of_hash.html"
-stash.set('ll', [ { 'a': 9  }, { 'a': 1  }, { 'a': 3  } ]);
-//line 4 "list_of_hash.html"
-
-// FOREACH 
-(function() {
-    var list = stash.get(['ll', 0, 'sort', [ 'a' ]]);
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['hash'] = value;
-//line 3 "list_of_hash.html"
-output += stash.get(['hash', 0, 'a', 0]);
-output += ':';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['localise1.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "localise1.html"
-output += stash.get('thing');
-output += '\n';
-//line 2 "localise1.html"
-stash.set('thing', 'bar');
-//line 3 "localise1.html"
-output += stash.get('thing');
-output += '\n';
-//line 4 "localise1.html"
-output += context.include('myblock');
-//line 5 "localise1.html"
-output += stash.get('thing');
-output += '\n';
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['myblock'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 7 "localise1.html"
-stash.set('thing', 'baz');
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['localise2.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "localise2.html"
-output += stash.get('thing');
-output += '\n';
-//line 2 "localise2.html"
-stash.set('thing', 'bar');
-//line 3 "localise2.html"
-output += stash.get('thing');
-output += '\n';
-//line 4 "localise2.html"
-output += context.process('myblock');
-//line 5 "localise2.html"
-output += stash.get('thing');
-output += '\n';
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['myblock'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 7 "localise2.html"
-stash.set('thing', 'baz');
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['loop1.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "loop1.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 1, 2, 3 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 2 "loop1.html"
-output += stash.get('i');
-output += ':';
-//line 2 "loop1.html"
-output += stash.get(['loop', 0, 'index', 0]);
-output += '\n';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['loop2.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 9 "loop2.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 1, 2, 3 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 4 "loop2.html"
-if (stash.get(['loop', 0, 'first', 0])) {
-output += '[start]';
-}
-
-//line 5 "loop2.html"
-output += stash.get('i');
-//line 8 "loop2.html"
-if (stash.get(['loop', 0, 'last', 0])) {
-output += '[end]';
-}
-;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['loop3.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "loop3.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 1, 2, 3 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 2 "loop3.html"
-output += stash.get('i');
-output += ':';
-//line 2 "loop3.html"
-output += stash.get(['loop', 0, 'count', 0]);
-output += '\n';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['loop4.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "loop4.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 1, 2, 3 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 2 "loop4.html"
-output += stash.get('i');
-output += ':';
-//line 2 "loop4.html"
-output += stash.get(['loop', 0, 'size', 0]);
-output += ':';
-//line 2 "loop4.html"
-output += stash.get(['loop', 0, 'max', 0]);
-output += '\n';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['loop5.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "loop5.html"
-
-// FOREACH 
-(function() {
-    var list = [ 0, 1, 2, 3 ];
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 2 "loop5.html"
-output += stash.get('i');
-output += ':';
-//line 2 "loop5.html"
-output += stash.get(['loop', 0, 'prev', 0]);
-output += ':';
-//line 2 "loop5.html"
-output += stash.get(['loop', 0, 'next', 0]);
-output += '\n';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-//line 6 "loop5.html"
-
-// FOREACH 
-(function() {
-    var list = { 'a': 0, 'b': 1, 'c': 3, 'd': 4  };
-    list = new Jemplate.Iterator(list);
-    var retval = list.get_first();
-    var value = retval[0];
-    var done = retval[1];
-    var oldloop;
-    try { oldloop = stash.get('loop') } finally {}
-    stash.set('loop', list);
-    try {
-        while (! done) {
-            stash.data['i'] = value;
-//line 5 "loop5.html"
-output += stash.get('i');
-output += ':';
-//line 5 "loop5.html"
-output += stash.get(['loop', 0, 'prev', 0]);
-output += ':';
-//line 5 "loop5.html"
-output += stash.get(['loop', 0, 'next', 0]);
-output += '\n';;
-            retval = list.get_next();
-            value = retval[0];
-            done = retval[1];
-        }
-    }
-    catch(e) {
-        throw(context.set_error(e, output));
-    }
-    stash.set('loop', oldloop);
-})();
-
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['myhashname.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "myhashname.html"
-stash.set( "object", { name: 'Wally' } );
-output += 'Hello, ';
-//line 4 "myhashname.html"
-output += stash.get(['object', 0, 'name', 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['myobjectnameac.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 4 "myobjectnameac.html"
-var obj = new TheName('Bally');
-stash.set( "objectName", obj.getName() );
-output += 'Hello, ';
-//line 5 "myobjectnameac.html"
-output += stash.get('objectName');
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['myobjectnameac2.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "myobjectnameac2.html"
-stash.set( "object", new TheName('Mally') );
-output += 'Hello, ';
-//line 4 "myobjectnameac2.html"
-output += stash.get(['object', 0, 'getName', []]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['myobjectnameattr.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "myobjectnameattr.html"
-stash.set( "object", new TheName('Larry') );
-output += 'Hello, ';
-//line 4 "myobjectnameattr.html"
-output += stash.get(['object', 0, 'name', 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['operator1.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "operator1.html"
-if ('abc' == 'abc') {
-output += 'same';
-}
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['operator2.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 3 "operator2.html"
-if ('abc' != 'def') {
-output += 'not same';
-}
-
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['operator3.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "operator3.html"
-output += 'abc'  + 'def';
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['plugins_basic.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "plugins_basic.html"
-// USE
-stash.set('dummy', context.plugin('dummy', [ 'simple param' ]));
-//line 2 "plugins_basic.html"
-output += stash.get(['dummy', 0, 'simple', 0]);
-output += '\n#\n';
-//line 4 "plugins_basic.html"
-output += stash.get(['dummy', 0, 'params', [ 'one', 'two' ]]);
-output += '\n#\n';
-//line 6 "plugins_basic.html"
-output += stash.get(['dummy', 0, 'what', 0]);
-output += '\n#\n';
-//line 8 "plugins_basic.html"
-output += stash.get(['dummy', 0, 'get_what', 0]);
-output += '\n#\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['stash-functions1.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "stash-functions1.html"
-output += stash.get(['hash', 0, 'keys', 0, 'sort', 0, 'join', [ '+' ]]);
-output += '\n';
-//line 2 "stash-functions1.html"
-output += stash.get(['hash', 0, 'keys', [], 'sort', [], 'join', [ '+' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['stash-functions2.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "stash-functions2.html"
-output += stash.get(['hash', 0, 'keys', 0]);
-output += '\n';
-//line 2 "stash-functions2.html"
-output += stash.get(['hash', 0, 'keys', [], 'join', [ '+' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['stash-functions3.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "stash-functions3.html"
-output += stash.get(['hash', 0, 'noarg', 0]);
-output += '\n';
-//line 2 "stash-functions3.html"
-output += stash.get(['hash', 0, 'noarg', []]);
-output += '\n';
-//line 3 "stash-functions3.html"
-output += stash.get(['hash', 0, 'arg', [ 'abc' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_chunk.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_chunk.html"
-stash.set('a', '1234567890');
-//line 2 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', 0, 'join', [ ' ' ]]);
-output += '\n';
-//line 3 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ 2 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 4 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ 3 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 5 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ 9 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 6 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ 10 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 7 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ -1 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 8 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ -2 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 9 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ -3 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 10 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ -9 ], 'join', [ ' ' ]]);
-output += '\n';
-//line 11 "string_chunk.html"
-output += stash.get(['a', 0, 'chunk', [ -10 ], 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_defined.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_defined.html"
-stash.set('a', '1');
-//line 2 "string_defined.html"
-output += stash.get(['a', 0, 'defined', 0]) ? '1' : '0';
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_hash.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_hash.html"
-stash.set('a', 'Hi');
-//line 2 "string_hash.html"
-output += stash.get(['a', 0, 'hash', 0, 'value', 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_length.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_length.html"
-stash.set('a', 'Hi');
-//line 2 "string_length.html"
-output += stash.get(['a', 0, 'length', 0]);
-output += '\n';
-//line 3 "string_length.html"
-stash.set('a', 10);
-//line 4 "string_length.html"
-output += stash.get(['a', 0, 'length', 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_list.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_list.html"
-stash.set('a', 'Hi');
-//line 2 "string_list.html"
-output += stash.get(['a', 0, 'list', 0, 0, 0]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_match.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_match.html"
-stash.set('a', 'aaa12aaa34aaa56');
-//line 2 "string_match.html"
-output += stash.get(['a', 0, 'match', [ '\\\d\\\d' ], 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_repeat.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_repeat.html"
-stash.set('a', 'aaa');
-//line 2 "string_repeat.html"
-output += stash.get(['a', 0, 'repeat', [ 3 ]]);
-output += '\n';
-//line 3 "string_repeat.html"
-output += stash.get(['a', 0, 'repeat', []]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_replace.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_replace.html"
-stash.set('a', 'aaa12aaa34aaa56');
-//line 2 "string_replace.html"
-output += stash.get(['a', 0, 'replace', [ '\\\d\\\d', 'bb' ]]);
-output += '\n';
-//line 3 "string_replace.html"
-output += stash.get(['a', 0, 'replace', [ '\\\d\\\d' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_search.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_search.html"
-stash.set('a', 'aaa12aaa34aaa56');
-//line 2 "string_search.html"
-output += stash.get(['a', 0, 'search', [ '\\\d\\\d' ]]) ? 1 : 0;
-output += '\n';
-//line 3 "string_search.html"
-output += stash.get(['a', 0, 'search', [ 'w' ]]) ? 1 : 0;
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_size.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_size.html"
-stash.set('a', '1');
-//line 2 "string_size.html"
-output += stash.get(['a', 0, 'defined', 0]) ? '1' : '0';
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['string_split.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "string_split.html"
-stash.set('a', 'aaa12aaa34aaa');
-//line 2 "string_split.html"
-output += stash.get(['a', 0, 'split', [ '\\\d\\\d' ], 'join', [ ' ' ]]);
-output += '\n';
-//line 3 "string_split.html"
-stash.set('a', '1aaa2aaa3aaa4');
-//line 4 "string_split.html"
-output += stash.get(['a', 0, 'split', [ 'aaa' ], 'join', [ ' ' ]]);
-output += '\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['test.html'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-//line 1 "test.html"
-stash.set('testhash', {   });
-//line 2 "test.html"
-stash.set('testkey', 'bob');
-//line 3 "test.html"
-stash.set(['testhash', 0, stash.get('testkey'), 0], 'bozo');
-output += 'Value: ';
-//line 4 "test.html"
-output += stash.get(['testhash', 0, stash.get('testkey'), 0]);
-output += '<br />\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
@@ -1 +0,0 @@
-Howdy <! name !>. How are you?
@@ -1 +0,0 @@
-Hola <! name !>. Como estas?
@@ -1,3 +0,0 @@
-English: <! INCLUDE greeting/english !>
-
-Spanish: <! InCluDe greeting/spanish !>
@@ -1 +0,0 @@
-Hello, <! name !>!
@@ -1,89 +0,0 @@
-/*
-   This JavaScript code was generated by Jemplate, the JavaScript
-   Template Toolkit. Any changes made to this file will be lost the next
-   time the templates are compiled.
-
-   Copyright 2006 - Ingy döt Net - All rights reserved.
-*/
-
-if (typeof(Jemplate) == 'undefined')
-    throw('Jemplate.js must be loaded before any Jemplate template files');
-
-Jemplate.templateMap['greeting/english'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += 'Howdy ';
-//line 1 "greeting/english"
-output += stash.get('name');
-output += '. How are you?\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['greeting/spanish'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += 'Hola ';
-//line 1 "greeting/spanish"
-output += stash.get('name');
-output += '. Como estas?\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['greetings'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += 'English: ';
-//line 1 "greetings"
-output += context.include('greeting/english');
-output += '\nSpanish: ';
-//line 3 "greetings"
-output += context.include('greeting/spanish');
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
-Jemplate.templateMap['hello'] = function(context) {
-    if (! context) throw('Jemplate function called without context\n');
-    var stash = context.stash;
-    var output = '';
-
-    try {
-output += 'Hello, ';
-//line 1 "hello"
-output += stash.get('name');
-output += '!\n';
-    }
-    catch(e) {
-        var error = context.set_error(e, output);
-        throw(error);
-    }
-
-    return output;
-}
-
@@ -1,18 +0,0 @@
-if (typeof dummy == 'undefined') {
-    dummy = function() {
-        this.context = arguments[0];
-        this.what = arguments[1];
-    };
-}
-
-dummy.prototype.simple = function() {
-    return 'This text came from the plugin';
-}
-
-dummy.prototype.params = function(one, two) {
-    return 'params: '+one+', '+two;
-}
-
-dummy.prototype.get_what = function() {
-    return 'what: ' + this.what;
-}
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../join.t.js';
-    </script>
-    <script type="text/javascript" src="../join.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,90 +0,0 @@
-/*==============================================================================
-Subclass - Define a Class potentially as a Subclass in JavaScript
-
-DESCRIPTION:
-
-
-
-AUTHORS:
-
-    Ingy döt Net <ingy@cpan.org>
-
-COPYRIGHT:
-
-Copyright Ingy döt Net 2006. All rights reserved.
-
-Subclass.js is free software. 
-
-This library is free software; you can redistribute it and/or modify it
-under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or (at
-your option) any later version.
-
-This library is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-General Public License for more details.
-
-    http://www.gnu.org/copyleft/lesser.txt
-
- =============================================================================*/
-
-/*==============================================================================
-Subclass - this can be used to create new classes
- =============================================================================*/
-
-Subclass = function(name, base) {
-    if (!name) die("Can't create a subclass without a name");
-
-    var parts = name.split('.');
-    var subclass = window;
-    for (var i = 0; i < parts.length; i++) {
-        if (! subclass[parts[i]])
-            subclass[parts[i]] = function() {
-                try { this.init() } catch(e) {}
-            };
-        subclass = subclass[parts[i]];
-    }
-
-    if (base) {
-        var baseclass = eval('new ' + base + '()');
-        subclass.prototype = baseclass;
-        subclass.prototype.baseclass = base;
-        subclass.prototype.superfunc = Subclass.generate_superfunc();
-    }
-    subclass.prototype.classname = name;
-    return subclass.prototype;
-}
-
-Subclass.generate_superfunc = function() {
-    return function(func) {
-        var p;
-        var found = false;
-        var caller_func = arguments.callee.caller;
-        for (var b = this.classname; b; b = p.baseclass) {
-            p = eval(b + '.prototype');
-            if (! found) {
-                if (p[func] && p[func] == caller_func)
-                    found = true;
-                continue;
-            }
-            if (p[func] && p[func] != caller_func)
-                return p[func];
-        }
-        die(
-            "No superfunc function for: " + func + "\n" +
-            "baseclass was: " + this.baseclass + "\n" +
-            "caller was: " + arguments.callee.caller
-        );
-    }
-}
-
-/*
-
-=head1 NAME
-
-Subclass - Define a class or subclass
-
-=cut
-
-*/
@@ -1,595 +0,0 @@
-proto = Subclass('Test.Base');
-
-Test.Base.VERSION = '0.13';
-
-proto.init = function() {
-    this.builder = Test.Builder.instance();
-    this.builder.reset();
-    this.block_class = 'Test.Base.Block';
-    this.state = {};
-    this.state.compiled = false;
-    this.state.spec_url = testBaseCurrentScript;
-    this.state.spec_content = null;
-    this.state.filters_map = {};
-    this.state.blocks = [];
-}
-
-proto.spec = function(url) {
-    this.state.spec_url = url;
-}
-
-proto.filters = function(obj) {
-    this.state.filters_map = obj;
-}
-
-proto.run_is = function(x, y) {
-    try {
-        this.compile();
-        var blocks =  this.state.blocks;
-        for (var i = 0; i < blocks.length; i++) {
-            var block = blocks[i];
-            if (! this.verify_block(block, x, y)) continue;
-            this.is(block.data[x], block.data[y], block.name);
-        }
-    }
-    catch(e) {
-        // alert(e);
-        throw(e);
-    }
-}
-
-proto.plan = function(number) {
-    var cmds = {tests: number};
-    return this.builder.plan(cmds);
-}
-
-proto.pass = function(name) {
-    return this.builder.ok(true, name);
-}
-
-proto.fail = function(name) {
-    return this.builder.ok(false, name);
-}
-
-proto.is = function (got, expect, desc) {
-    return this.builder.isEq(got, expect, desc);
-};
-
-proto.isnt = function (got, expect, desc) {
-    return this.builder.isntEq(got, expect, desc);
-};
-
-proto.like = function (val, regex, desc) {
-    return this.builder.like(val, regex, desc);
-};
-
-proto.unlike = function (val, regex, desc) {
-    return this.builder.unlike(val, regex, desc);
-};
-
-proto.compile = function() {
-    if (this.state.compiled) return;
-    this.get_spec();
-    this.create_blocks();
-    this.state.compiled = true;
-}
-
-proto.get_spec = function() {
-    var url = this.state.spec_url;
-    if (url == undefined)
-        throw('no spec provided');
-
-    var text = Ajax.get(url);
-    text = text.replace(/(?:.|\n)*\/\*\s*test.*\n/i, '');
-    text = text.replace(/\n\*\/(?:.|\n)*/, '');
-    this.state.spec_content = text;
-}
-
-proto.create_blocks = function() {
-    var text = this.state.spec_content;
-    // This is what we want but Safari is broken with ^ and m flag
-    // var hunks = text.split(/(?=(\A|^)===)/m);
-    // This works for now but is too fragile.
-    var hunks = text.split(/(?====)/);
-    for (var i = 0; i < hunks.length; i++) {
-        var hunk = hunks[i];
-        if (! hunk.match(/^===/)) continue;
-        var block = this.make_block(hunk);
-        this.state.blocks.push(block);
-    }
-}
-
-proto.make_block = function(hunk) {
-    var block = eval('new ' + this.block_class + '()');
-    if (! hunk.match(/^===/)) throw("Invalid Hunk");
-
-    var index = hunk.indexOf('\n') + 1;
-    if (! index) throw('Invalid Hunk.');
-    var name = hunk.substr(4, index - 5);
-    hunk = hunk.substr(index); 
-    block.name = name.replace(/^\s*(.*?)\s*$/, '$1');
-
-    var chunks = [];
-    while (hunk.indexOf('\n---') >= 0) {
-        index = hunk.indexOf('\n---') + 1;
-        var chunk = hunk.substr(0, index);
-        hunk = hunk.substr(index);
-        chunks.push(chunk);
-    }
-    chunks.push(hunk);
-
-    for (var i = 0; i < chunks.length; i++) {
-        var chunk = chunks[i];
-        index = chunk.indexOf('\n');
-        if (index < 0) throw('xxx1');
-        var line1 = chunk.substr(0, index);
-        var section_data = chunk.substr(index + 1);
-        line1 = line1.replace(/^---\s*/, '');
-        if (! line1.length) throw('xxx2');
-        var section_name = '';
-        var section_filters = [];
-        if (line1.indexOf(':') >= 0) {
-            index = line1.indexOf(':');
-            section_data = line1.substr(index + 1).
-                replace(/^\s*(.*?)\s*$/, '$1');
-            line1 = line1.substr(0, index);
-        }
-        if (! line1.match(/^\w+$/)) throw('xxx3');
-        section_name = line1;
-        block.add_section(section_name, section_filters, section_data);
-    }
-    return block;
-}
-
-proto.verify_block = function(block) {
-    block.apply_filters(this.state.filters_map);
-    for (var i = 1; i < arguments.length; i++) {
-        var value = arguments[i];
-        if (typeof block.data[value] == 'undefined') return false;
-    }
-    return true;
-}
-
-//------------------------------------------------------------------------------
-proto = Subclass('Test.Base.Block');
-
-proto.init = function() {
-    this.name = null;
-    this.description = null;
-    this.sections = [];
-    this.data = {};
-    this.filters = {};
-    this.filter_object = new Test.Base.Filter();
-}
-
-proto.add_section = function(name, filters, data) {
-    this.sections.push(name);
-    this.data[name] = data;
-    this.filters[name] = filters;
-}
-
-proto.apply_filters = function(filter_overrides) {
-    var sections = this.sections;
-    for (var i = 0; i < sections.length; i++) {
-        var section = sections[i];
-        var filters = ['normalize', 'trim'];
-        this.push_filters(filters, this.filters[section]);
-        this.push_filters(filters, filter_overrides[section]);
-        this.filter_section(section, filters);
-    }
-}
-
-proto.push_filters = function(a1, a2) {
-    if (typeof a2 == 'undefined')
-        return;
-    if (typeof a2 == 'string')
-        a1.push(a2);
-    else {
-        for (var i = 0; i < a2.length; i++) {
-            a1.push(a2[i]);
-        }
-    }
-}
-
-proto.filter_section = function(section, filters) {
-    var data = this.data[section];
-    for (var i = 0; i < filters.length; i++) {
-        var filter = filters[i];
-        if (typeof window[filter] == 'function')
-            data = (window[filter]).call(this, data, this);
-        else if (typeof this.filter_object[filter] == 'function')
-            data = (this.filter_object[filter]).call(this, data, this);
-        else
-            throw('No function for filter: ' + filter);
-    }
-    this.data[section] = data;
-}
-
-//------------------------------------------------------------------------------
-proto = Subclass('Test.Base.Filter');
-
-proto.ajax_get = function(url) {
-    url = url.replace(/n+$/, '');
-    return Ajax.get(url);
-}
-
-proto.trim = function(content, block) {
-    var result = content.replace(/^\s*\n/, '');
-    result = result.replace(/\n\s*$/, '\n');
-    return result;
-}
-
-proto.normalize = function(content, block) {
-    return content;
-}
-
-proto.evaluate = function(content, block) {
-    var javascript = content;
-    var object = JSON.parse(javascript);
-    return object;
-}
-
-//------------------------------------------------------------------------------
-// Debugging Support
-//------------------------------------------------------------------------------
-
-function XXX(msg) {
-    //if (! confirm(arguments.join('\n')))
-    if (! confirm(msg))
-        throw("terminated...");
-}
-
-function JJJ(obj) {
-    XXX(JSON.stringify(obj));
-}
-
-//------------------------------------------------------------------------------
-// Ajax support
-//------------------------------------------------------------------------------
-if (! this.Ajax) Ajax = {};
-
-Ajax.get = function(url, callback) {
-    var req = new XMLHttpRequest();
-    req.open('GET', url, Boolean(callback));
-    return Ajax._send(req, null, callback, url);
-}
-
-Ajax.post = function(url, data, callback) {
-    var req = new XMLHttpRequest();
-    req.open('POST', url, Boolean(callback));
-    req.setRequestHeader(
-        'Content-Type', 
-        'application/x-www-form-urlencoded'
-    );
-    return Ajax._send(req, data, callback, url);
-}
-
-Ajax._send = function(req, data, callback, url) {
-    if (callback) {
-        req.onreadystatechange = function() {
-            if (req.readyState == 4) {
-                if(req.status == 200)
-                    callback(req.responseText);
-            }
-        };
-    }
-    req.send(data);
-    if (!callback) {
-        if (req.status != 200)
-            throw('Request for "' + url +
-                  '" failed with status: ' + req.status);
-        return req.responseText;
-    }
-}
-
-//------------------------------------------------------------------------------
-// Cross-Browser XMLHttpRequest v1.1
-//------------------------------------------------------------------------------
-/*
-Emulate Gecko 'XMLHttpRequest()' functionality in IE and Opera. Opera requires
-the Sun Java Runtime Environment <http://www.java.com/>.
-
-by Andrew Gregory
-http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/
-
-This work is licensed under the Creative Commons Attribution License. To view a
-copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or send
-a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305,
-USA.
-*/
-
-// IE support
-if (window.ActiveXObject && !window.XMLHttpRequest) {
-  window.XMLHttpRequest = function() {
-    return new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') != -1) ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
-  };
-}
-
-// Opera support
-if (window.opera && !window.XMLHttpRequest) {
-  window.XMLHttpRequest = function() {
-    this.readyState = 0; // 0=uninitialized,1=loading,2=loaded,3=interactive,4=complete
-    this.status = 0; // HTTP status codes
-    this.statusText = '';
-    this._headers = [];
-    this._aborted = false;
-    this._async = true;
-    this.abort = function() {
-      this._aborted = true;
-    };
-    this.getAllResponseHeaders = function() {
-      return this.getAllResponseHeader('*');
-    };
-    this.getAllResponseHeader = function(header) {
-      var ret = '';
-      for (var i = 0; i < this._headers.length; i++) {
-        if (header == '*' || this._headers[i].h == header) {
-          ret += this._headers[i].h + ': ' + this._headers[i].v + '\n';
-        }
-      }
-      return ret;
-    };
-    this.setRequestHeader = function(header, value) {
-      this._headers[this._headers.length] = {h:header, v:value};
-    };
-    this.open = function(method, url, async, user, password) {
-      this.method = method;
-      this.url = url;
-      this._async = true;
-      this._aborted = false;
-      if (arguments.length >= 3) {
-        this._async = async;
-      }
-      if (arguments.length > 3) {
-        // user/password support requires a custom Authenticator class
-        opera.postError('XMLHttpRequest.open() - user/password not supported');
-      }
-      this._headers = [];
-      this.readyState = 1;
-      if (this.onreadystatechange) {
-        this.onreadystatechange();
-      }
-    };
-    this.send = function(data) {
-      if (!navigator.javaEnabled()) {
-        alert("XMLHttpRequest.send() - Java must be installed and enabled.");
-        return;
-      }
-      if (this._async) {
-        setTimeout(this._sendasync, 0, this, data);
-        // this is not really asynchronous and won't execute until the current
-        // execution context ends
-      } else {
-        this._sendsync(data);
-      }
-    }
-    this._sendasync = function(req, data) {
-      if (!req._aborted) {
-        req._sendsync(data);
-      }
-    };
-    this._sendsync = function(data) {
-      this.readyState = 2;
-      if (this.onreadystatechange) {
-        this.onreadystatechange();
-      }
-      // open connection
-      var url = new java.net.URL(new java.net.URL(window.location.href), this.url);
-      var conn = url.openConnection();
-      for (var i = 0; i < this._headers.length; i++) {
-        conn.setRequestProperty(this._headers[i].h, this._headers[i].v);
-      }
-      this._headers = [];
-      if (this.method == 'POST') {
-        // POST data
-        conn.setDoOutput(true);
-        var wr = new java.io.OutputStreamWriter(conn.getOutputStream());
-        wr.write(data);
-        wr.flush();
-        wr.close();
-      }
-      // read response headers
-      // NOTE: the getHeaderField() methods always return nulls for me :(
-      var gotContentEncoding = false;
-      var gotContentLength = false;
-      var gotContentType = false;
-      var gotDate = false;
-      var gotExpiration = false;
-      var gotLastModified = false;
-      for (var i = 0; ; i++) {
-        var hdrName = conn.getHeaderFieldKey(i);
-        var hdrValue = conn.getHeaderField(i);
-        if (hdrName == null && hdrValue == null) {
-          break;
-        }
-        if (hdrName != null) {
-          this._headers[this._headers.length] = {h:hdrName, v:hdrValue};
-          switch (hdrName.toLowerCase()) {
-            case 'content-encoding': gotContentEncoding = true; break;
-            case 'content-length'  : gotContentLength   = true; break;
-            case 'content-type'    : gotContentType     = true; break;
-            case 'date'            : gotDate            = true; break;
-            case 'expires'         : gotExpiration      = true; break;
-            case 'last-modified'   : gotLastModified    = true; break;
-          }
-        }
-      }
-      // try to fill in any missing header information
-      var val;
-      val = conn.getContentEncoding();
-      if (val != null && !gotContentEncoding) this._headers[this._headers.length] = {h:'Content-encoding', v:val};
-      val = conn.getContentLength();
-      if (val != -1 && !gotContentLength) this._headers[this._headers.length] = {h:'Content-length', v:val};
-      val = conn.getContentType();
-      if (val != null && !gotContentType) this._headers[this._headers.length] = {h:'Content-type', v:val};
-      val = conn.getDate();
-      if (val != 0 && !gotDate) this._headers[this._headers.length] = {h:'Date', v:(new Date(val)).toUTCString()};
-      val = conn.getExpiration();
-      if (val != 0 && !gotExpiration) this._headers[this._headers.length] = {h:'Expires', v:(new Date(val)).toUTCString()};
-      val = conn.getLastModified();
-      if (val != 0 && !gotLastModified) this._headers[this._headers.length] = {h:'Last-modified', v:(new Date(val)).toUTCString()};
-      // read response data
-      var reqdata = '';
-      var stream = conn.getInputStream();
-      if (stream) {
-        var reader = new java.io.BufferedReader(new java.io.InputStreamReader(stream));
-        var line;
-        while ((line = reader.readLine()) != null) {
-          if (this.readyState == 2) {
-            this.readyState = 3;
-            if (this.onreadystatechange) {
-              this.onreadystatechange();
-            }
-          }
-          reqdata += line + '\n';
-        }
-        reader.close();
-        this.status = 200;
-        this.statusText = 'OK';
-        this.responseText = reqdata;
-        this.readyState = 4;
-        if (this.onreadystatechange) {
-          this.onreadystatechange();
-        }
-        if (this.onload) {
-          this.onload();
-        }
-      } else {
-        // error
-        this.status = 404;
-        this.statusText = 'Not Found';
-        this.responseText = '';
-        this.readyState = 4;
-        if (this.onreadystatechange) {
-          this.onreadystatechange();
-        }
-        if (this.onerror) {
-          this.onerror();
-        }
-      }
-    };
-  };
-}
-// ActiveXObject emulation
-if (!window.ActiveXObject && window.XMLHttpRequest) {
-  window.ActiveXObject = function(type) {
-    switch (type.toLowerCase()) {
-      case 'microsoft.xmlhttp':
-      case 'msxml2.xmlhttp':
-        return new XMLHttpRequest();
-    }
-    return null;
-  };
-}
-
-
-//------------------------------------------------------------------------------
-// JSON Support
-//------------------------------------------------------------------------------
-
-/*
-Copyright (c) 2005 JSON.org
-*/
-var JSON = function () {
-    var m = {
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        },
-        s = {
-            'boolean': function (x) {
-                return String(x);
-            },
-            number: function (x) {
-                return isFinite(x) ? String(x) : 'null';
-            },
-            string: function (x) {
-                if (/["\\\x00-\x1f]/.test(x)) {
-                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
-                        var c = m[b];
-                        if (c) {
-                            return c;
-                        }
-                        c = b.charCodeAt();
-                        return '\\u00' +
-                            Math.floor(c / 16).toString(16) +
-                            (c % 16).toString(16);
-                    });
-                }
-                return '"' + x + '"';
-            },
-            object: function (x) {
-                if (x) {
-                    var a = [], b, f, i, l, v;
-                    if (x instanceof Array) {
-                        a[0] = '[';
-                        l = x.length;
-                        for (i = 0; i < l; i += 1) {
-                            v = x[i];
-                            f = s[typeof v];
-                            if (f) {
-                                v = f(v);
-                                if (typeof v == 'string') {
-                                    if (b) {
-                                        a[a.length] = ',';
-                                    }
-                                    a[a.length] = v;
-                                    b = true;
-                                }
-                            }
-                        }
-                        a[a.length] = ']';
-                    } else if (x instanceof Object) {
-                        a[0] = '{';
-                        for (i in x) {
-                            v = x[i];
-                            f = s[typeof v];
-                            if (f) {
-                                v = f(v);
-                                if (typeof v == 'string') {
-                                    if (b) {
-                                        a[a.length] = ',';
-                                    }
-                                    a.push(s.string(i), ':', v);
-                                    b = true;
-                                }
-                            }
-                        }
-                        a[a.length] = '}';
-                    } else {
-                        return;
-                    }
-                    return a.join('');
-                }
-                return 'null';
-            }
-        };
-    return {
-        copyright: '(c)2005 JSON.org',
-        license: 'http://www.crockford.com/JSON/license.html',
-        stringify: function (v) {
-            var f = s[typeof v];
-            if (f) {
-                v = f(v);
-                if (typeof v == 'string') {
-                    return v;
-                }
-            }
-            return null;
-        },
-        parse: function (text) {
-            try {
-                return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
-                        text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
-                    eval('(' + text + ')');
-            } catch (e) {
-                return false;
-            }
-        }
-    };
-}();
-
@@ -1,841 +0,0 @@
-// # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
-
-// Set up namespace.
-if (typeof self != 'undefined') {
-    // Browser
-    if (typeof Test == 'undefined') Test = {PLATFORM: 'browser'};
-    else Test.PLATFORM = 'browser';
-} else if (typeof _global != 'undefined') {
-    //Director
-    if (typeof _global.Test == "undefined") _global.Test = {PLATFORM: 'director'};
-    else _global.Test.PLATFORM = 'director';
-} else {
-    throw new Error("Test.More does not support your platform");
-}
-
-// Constructor.
-Test.Builder = function () {
-    Test.Builder.Instances.push(this.reset());
-    if (!Test.Builder.Test) Test.Builder.Test = this;
-};
-
-// Static variables.
-Test.Builder.globalScope = typeof JSAN != 'undefined'
-  ? JSAN.globalScope
-  :  typeof window != 'undefined'
-    ? window
-    : typeof _global != 'undefined'
-      ? _global
-      : null;
-
-Test.Builder.VERSION = '0.21';
-Test.Builder.Instances = [];
-Test.Builder.lineEndingRx = /\r?\n|\r/g;
-Test.Builder.StringOps = {
-    eq: '==',
-    ne: '!=',
-    lt: '<',
-    gt: '>',
-    ge: '>=',
-    le: '<='
-};
-
-// Stoopid IE.
-Test.Builder.LF = typeof navigator != "undefined"
-    && navigator.userAgent.toLowerCase().indexOf('msie') + 1
-    && Test.Builder.globalScope.opera == undefined
-  ? "\r"
-  : "\n";
-
-// Static methods.
-Test.Builder.die = function (msg) {
-    throw new Error(msg);
-};
-
-Test.Builder._whoa = function (check, desc) {
-    if (!check) return;
-    Test.Builder.die("WHOA! " + desc + Test.Builder.LF +
-                     + "This should never happen! Please contact the author "
-                     + "immediately!");
-};
-
-Test.Builder.typeOf = function (object) {
-    var c = Object.prototype.toString.apply(object);
-    var name = c.substring(8, c.length - 1);
-    if (name != 'Object') return name;
-    // It may be a non-core class. Try to extract the class name from
-    // the constructor function. This may not work in all implementations.
-    if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
-        return RegExp.$1;
-    }
-    // No idea. :-(
-    return name;
-};
-
-Test.Builder.instance = function () {
-    if (!Test.Builder.Test) return new Test.Builder();
-    return Test.Builder.Test;
-};
-
-Test.Builder.create = function () {
-    var ret = new Test.Builder();
-    ret.diag(
-        "Test.Builder.create() has been deprecated. "
-        + "Use new Test.Builder() instead"
-    );
-    return ret;
-};
-
-// Instance methods.
-
-Test.Builder.prototype.reset = function () {
-    this.TestDied      = false;
-    this.HavePlan      = false;
-    this.NoPlan        = false;
-    this.CurrTest      = 0;
-    this.ExpectedTests = 0;
-    this.UseNums       = true;
-    this.NoHeader      = false;
-    this.NoEnding      = false;
-    this.TestResults   = [];
-    this.ToDo          = [];
-    this.Buffer        = [];
-    this.asyncs        = [0];
-    this.asyncID       = 0;
-    return this._setupOutput();
-};
-
-Test.Builder.prototype._print = function (msg) {
-    this.output().call(this, msg);
-};
-
-Test.Builder.prototype.warn = function (msg) {
-    this.warnOutput().apply(this, arguments);
-};
-
-Test.Builder.prototype.plan = function (arg) {
-    if (!arg) return;
-    if (this.HavePlan) Test.Builder.die("You tried to plan twice!");
-
-    if (!(arg instanceof Object))
-        Test.Builder.die("plan() doesn't understand " + arg);
-    for (var cmd in arg) {
-        if (cmd == 'tests') {
-            if (arg[cmd] == null) {
-                TestBulder.die(
-                    "Got an undefined number of tests. Looks like you tried to "
-                    + "say how many tests you plan to run but made a mistake."
-                    + Test.Builder.LF
-                );
-            } else if (!arg[cmd]) {
-                Test.Builder.die(
-                    "You said to run 0 tests! You've got to run something."
-                    + Test.Builder.LF
-                );
-            } else {
-                this.expectedTests(arg[cmd]);
-            }
-        } else if (cmd == 'skipAll') {
-            this.skipAll(arg[cmd]);
-        } else if (cmd == 'noPlan' && arg[cmd]) {
-            this.noPlan();
-        } else {
-            // Do nothing, since Object.prototype might have been changed.
-            // Too bad JS doesn't have real hashes!
-            // Test.Builder.die("plan() doesn't understand "
-            // + cmd + (arg[cmd] ? (" " + arg[cmd]) : ''));
-        }
-    }
-};
-
-Test.Builder.prototype.expectedTests = function (max) {
-    if (max) {
-        if (isNaN(max)) {
-            Test.Builder.die(
-                "Number of tests must be a postive integer. You gave it '"
-                + max + "'." + Test.Builder.LF
-            );
-        }
-
-        this.ExpectedTests = max.valueOf();
-        this.HavePlan       = true;
-        if (!this.noHeader()) this._print("1.." + max + Test.Builder.LF);
-    }
-    return this.ExpectedTests;
-};
-
-Test.Builder.prototype.noPlan = function () {
-    this.NoPlan   = 1;
-    this.HavePlan = 1;
-};
-
-Test.Builder.prototype.hasPlan = function () {
-    if (this.ExpectedTests) return this.ExpectedTests;
-    if (this.NoPlan) return 'noPlan';
-};
-
-Test.Builder.prototype.skipAll = function (reason) {
-    var out = "1..0";
-    if (reason) out += " # Skip " + reason;
-    out += Test.Builder.LF;
-    this.SkipAll = 1;
-    if (!this.noHeader()) this.output()(out);
-    // Just throw and catch an exception.
-    Test.Builder.globalScope.onerror = function () { return true; }
-    throw new Error("__SKIP_ALL__");
-};
-
-Test.Builder.prototype.ok = function (test, desc) {
-    // test might contain an object that we don't want to accidentally
-    // store, so we turn it into a boolean.
-    test = !!test;
-
-    if (!this.HavePlan) {
-        Test.Builder.die(
-            "You tried to run a test without a plan! Gotta have a plan."
-        );
-    }
-
-    // Append any output to the previous test's results.
-    if (this.Buffer.length && this.TestResults.length) {
-        this.TestResults[this.TestResults.length - 1].output +=
-            this.Buffer.splice(0, this.Buffer.length).join('');
-    
-    }
-
-    // I don't think we need to worry about threading in JavaScript.
-    this.CurrTest++;
-
-    // In case desc is a string overloaded object, force it to stringify.
-    if (desc) desc = desc.toString();
-
-    var startsNumber
-    if (desc != null && /^[\d\s]+$/.test(desc)) {
-        this.diag( "Your test description is '" + desc + "'. You shouldn't use",
-                   Test.Builder.LF,
-                   "numbers for your test names. Very confusing.");
-    }
-
-    var todo = this._todo();
-    // I don't think we need to worry about result beeing shared between
-    // threads.
-    var out = '';
-    var result = {};
-
-    if (test) {
-        result.ok        = true;
-        result.actual_ok = test;
-    } else {
-        out += 'not ';
-        result.ok        = todo ? true : false;
-        result.actual_ok = false;
-    }
-
-    out += 'ok';
-    if (this.useNumbers) out += ' ' + this.CurrTest;
-
-    if (desc == null) {
-        result.desc = '';
-    } else {
-        desc = desc.replace(Test.Builder.lineEndingRx, Test.Builder.LF + "# ");
-        // XXX Does this matter since we don't have a TestHarness?
-        desc.split('#').join('\\#'); // # # in a desc can confuse TestHarness.
-        out += ' - ' + desc;
-        result.desc = desc;
-    }
-
-    if (todo) {
-        todo = todo.replace(Test.Builder.lineEndingRx, Test.Builder.LF + "# ");
-        out += " # TODO " + todo;
-        result.reason = todo;
-        result.type   = 'todo';
-    } else {
-        result.reason = '';
-        result.type   = '';
-    }
-
-    this.TestResults[this.CurrTest - 1] = result;
-
-    out += Test.Builder.LF;
-    this._print(out);
-
-    if (!test) {
-        var msg = todo ? "Failed (TODO)" : "Failed";
-        this.diag("    " + msg + " test");
-    }
-    result.output = this.Buffer.splice(0, this.Buffer.length).join('');
-    return test;
-};
-
-Test.Builder.prototype.isEq = function (got, expect, desc) {
-    if (got == null || expect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '==', expect, desc);
-    }
-    return this.cmpOK(got, '==', expect, desc);
-};
-
-Test.Builder.prototype.isNum = function (got, expect, desc) {
-    if (got == null || expect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '==', expect, desc);
-    }
-    return this.cmpOK(Number(got), '==', Number(expect), desc);
-};
-
-Test.Builder.prototype.isntEq = function (got, dontExpect, desc) {
-    if (got == null || dontExpect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '!=', dontExpect, desc);
-    }
-    return this.cmpOK(got, '!=', dontExpect, desc);
-};
-
-Test.Builder.prototype.isntNum = function (got, dontExpect, desc) {
-    if (got == null || dontExpect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '!=', dontExpect, desc);
-    }
-    return this.cmpOK(Number(got), '!=', Number(dontExpect), desc);
-};
-
-Test.Builder.prototype.like = function (val, regex, desc) {
-    return this._regexOK(val, regex, '=~', desc);
-};
-
-Test.Builder.prototype.unlike = function (val, regex, desc) {
-    return this._regexOK(val, regex, '!~', desc);
-};
-
-Test.Builder.prototype._regexOK = function (val, regex, cmp, desc) {
-    // Create a regex object.
-    var type = Test.Builder.typeOf(regex);
-    var ok;
-    if (type.toLowerCase() == 'string') {
-        // Create a regex object.
-        regex = new RegExp(regex);
-    } else {
-        if (type != 'RegExp') {
-            ok = this.ok(false, desc);
-            this.diag("'" + regex + "' doesn't look much like a regex to me.");
-            return ok;
-        }
-    }
-
-    if (val == null || typeof val != 'string') {
-        if (cmp == '=~') {
-            // The test fails.
-            ok = this.ok(false, desc);
-            this._diagLike(val, regex, cmp);
-        } else {
-            // undefined matches nothing (unlike in Perl, where undef =~ //).
-            ok = this.ok(true, desc);
-        }
-        return ok;
-    }
-
-    // Use val.match() instead of regex.test() in case they've set g.
-    var test = val.match(regex);
-    if (cmp == '!~') test = !test;
-    ok = this.ok(test, desc);
-    if (!ok) this._diagLike(val, regex, cmp);
-    return ok;
-};
-
-Test.Builder.prototype._diagLike = function (val, regex, cmp) {
-    var match = cmp == '=~' ? "doesn't match" : "      matches";
-    return this.diag(
-        "                  '" + val + "" + Test.Builder.LF +
-        "    " + match + " /" + regex.source + "/"
-    );
-};
-
-Test.Builder.prototype.cmpOK = function (got, op, expect, desc) {
-
-    var test;
-    if (Test.Builder.StringOps[op]) {
-        // Force string context.
-        test = eval("got.toString() " + Test.Builder.StringOps[op] + " expect.toString()");
-    } else {
-        test = eval("got " + op + " expect");
-    }
-
-    var ok = this.ok(test, desc);
-    if (!ok) {
-        if (/^(eq|==)$/.test(op)) {
-            this._isDiag(got, op, expect);
-        } else {
-            this._cmpDiag(got, op, expect);
-        }
-    }
-    return ok;
-};
-
-Test.Builder.prototype._cmpDiag = function (got, op, expect) {
-    if (got != null) got = "'" + got.toString() + "'";
-    if (expect != null) expect = "'" + expect.toString() + "'";
-    return this.diag("    " + got + Test.Builder.LF + "        " + op
-                     + Test.Builder.LF + "    " + expect);
-};
-
-Test.Builder.prototype._isDiag = function (got, op, expect) {
-    var args = [got, expect];
-    for (var i = 0; i < args.length; i++) {
-        if (args[i] != null) {
-            args[i] = op == 'eq' ? "'" + args[i].toString() + "'" : args[i].valueOf();
-
-            args[i] = args[i].replace(/&/g, '&amp;'); 
-            args[i] = args[i].replace(/</g, '&lt;');
-            args[i] = args[i].replace(/>/g, '&gt;');
-            args[i] = args[i].replace(/"/g, '&quot;'); // " end quote for emacs
-        }
-    }
-
-
-    return this.diag(
-        "         got: \"" + args[0] + "\"" + Test.Builder.LF +
-        "    expected: \"" + args[1] + "\"" + Test.Builder.LF
-    );
-};
-
-Test.Builder.prototype.BAILOUT = function (reason) {
-    this._print("Bail out! " + reason);
-    // Just throw and catch an exception.
-    Test.Builder.globalScope.onerror = function () {
-        // XXX Do something to tell TestHarness it was a bailout?
-        return true;
-    }
-    throw new Error("__BAILOUT__");
-};
-
-Test.Builder.prototype.skip = function (why) {
-    if (!this.HavePlan)
-        Test.Builder.die("You tried to run a test without a plan! Gotta have a plan.");
-
-    // In case desc is a string overloaded object, force it to stringify.
-    if (why) why = why.toString().replace(Test.Builder.lineEndingRx,
-                                          Test.Builder.LF+ "# ");
-
-    this.CurrTest++;
-    this.TestResults[this.CurrTest - 1] = {
-        ok:        true,
-        actual_ok: true,
-        desc:      '',
-        type:      'skip',
-        reason:    why
-    };
-
-    var out = "ok";
-    if (this.useNumbers) out += ' ' + this.CurrTest;
-    out    += " # skip " + why + Test.Builder.LF;
-    this._print(out);
-    this.TestResults[this.CurrTest - 1].output =
-    this.Buffer.splice(0, this.Buffer.length).join('');
-    return true;
-};
-
-Test.Builder.prototype.todoSkip = function (why) {
-    if (!this.HavePlan)
-        Test.Builder.die("You tried to run a test without a plan! Gotta have a plan.");
-
-    // In case desc is a string overloaded object, force it to stringify.
-    if (why) why = why.toString().replace(Test.Builder.lineEndingRx,
-                                          Test.Builder.LF + "# ");
-    
-
-    this.CurrTest++;
-    this.TestResults[this.CurrTest - 1] = {
-        ok:        true,
-        actual_ok: false,
-        desc:      '',
-        type:      'todo_skip',
-        reason:    why
-    };
-
-    var out = "not ok";
-    if (this.useNumbers) out += ' ' + this.CurrTest;
-    out    += " # TODO & SKIP " + why + Test.Builder.LF;
-    this._print(out);
-    this.TestResults[this.CurrTest - 1].output =
-    this.Buffer.splice(0, this.Buffer.length).join('');
-    return true;
-};
-
-Test.Builder.prototype.skipRest = function (reason) {
-    var out = "# Skip";
-    if (reason) out += " " + reason;
-    out += Test.Builder.LF;
-    if (this.NoPlan) this.skip(reason);
-    else {
-        for (var i = this.CurrTest; i < this.ExpectedTests; i++) {
-            this.skip(reason);
-        }
-    }
-    // Just throw and catch an exception.
-    Test.Builder.globalScope.onerror = function () { return true; }
-    throw new Error("__SKIP_REST__");
-};
-
-Test.Builder.prototype.useNumbers = function (useNums) {
-    if (useNums != null) this.UseNums = useNums;
-    return this.UseNums;
-};
-
-Test.Builder.prototype.noHeader = function (noHeader) {
-    if (noHeader != null) this.NoHeader = !!noHeader;
-    return this.NoHeader;
-};
-
-Test.Builder.prototype.noEnding = function (noEnding) {
-    if (noEnding != null) this.NoEnding = !!noEnding;
-    return this.NoEnding;
-};
-
-Test.Builder.prototype.diag = function () {
-    if (!arguments.length) return;
-
-    var msg = '# ';
-    // Join each agument and escape each line with a #.
-    for (var i = 0; i < arguments.length; i++) {
-        // Replace any newlines.
-        msg += arguments[i].toString().replace(Test.Builder.lineEndingRx,
-                                               Test.Builder.LF + "# ");
-    }
-
-    // Append a new line to the end of the message if there isn't one.
-    if (!(new RegExp(Test.Builder.LF + '$').test(msg)))
-        msg += Test.Builder.LF;
-    // Append the diag message to the most recent result.
-    return this._printDiag(msg);
-};
-
-Test.Builder.prototype._printDiag = function () {
-    var fn = this.todo() ? this.todoOutput() : this.failureOutput();
-    fn.apply(this, arguments);
-    return false;
-};
-
-Test.Builder.prototype.output = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.Output = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.Output;
-};
-
-Test.Builder.prototype.failureOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.FailureOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.FailureOutput;
-};
-
-Test.Builder.prototype.todoOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.TodoOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.TodoOutput;
-};
-
-Test.Builder.prototype.endOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.EndOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.EndOutput;
-};
-
-Test.Builder.prototype.warnOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.WarnOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.WarnOutput;
-};
-
-Test.Builder.prototype._setupOutput = function () {
-    if (Test.PLATFORM == 'browser') {
-        var top = Test.Builder.globalScope;
-        var doc = top.document;
-        var writer = function (msg) {
-            // I'm sure that there must be a more efficient way to do this,
-            // but if I store the node in a variable outside of this function
-            // and refer to it via the closure, then things don't work right
-            // --the order of output can become all screwed up (see
-            // buffer.html).  I have no idea why this is.
-            var node = doc.getElementById("test");
-            var body = doc.body || doc.getElementsByTagName("body")[0];
-            if (node) {
-                // This approach is neater, but causes buffering problems when
-                // mixed with document.write. See tests/buffer.html.
-                //node.appendChild(document.createTextNode(msg));
-                //return;
-                for (var i = 0; i < node.childNodes.length; i++) {
-                    if (node.childNodes[i].nodeType == 3 /* Text Node */) {
-                        // Append to the node and scroll down.
-                        node.childNodes[i].appendData(msg);
-                        top.scrollTo(
-                            0, body.offsetHeight || body.scrollHeight
-                        );
-                        return;
-                    }
-                }
-
-                // If there was no text node, add one.
-                node.appendChild(doc.createTextNode(msg));
-                top.scrollTo(0, body.offsetHeight || body.scrollHeight);
-                return;
-            }
-
-            // Default to the normal write and scroll down...
-            doc.write("<pre style='margin: 0px'>"+msg+"</pre>");
-            // IE 6 Service Pack 2 requires that we re-cache the object. Bill
-            // Gates only knows why!
-            body = doc.body || doc.getElementsByTagName("body")[0];
-            if (body) top.scrollTo(0, body.offsetHeight || body.scrollHeight);
-        };
-
-        this.output(writer);
-        this.failureOutput(function (msg) {
-            writer('<span style="color: red; font-weight: bold">'
-                   + msg + '</span>')
-        });
-        this.todoOutput(writer);
-        this.endOutput(writer);
-
-        if (top.alert.apply) {
-            this.warnOutput(top.alert, top);
-        } else {
-            this.warnOutput(function (msg) { top.alert(msg); });
-        }
-
-    } else if (Test.PLATFORM == 'director') {
-        // Macromedia-Adobe:Director MX 2004 Support
-        // XXX Is _player a definitive enough object?
-        // There may be an even more explicitly Director object.
-        this.output(trace);       
-        this.failureOutput(trace);
-        this.todoOutput(trace);
-        this.warnOutput(trace);
-    }
-
-    return this;
-};
-
-Test.Builder.prototype.currentTest = function (num) {
-    if (num == null) return this.CurrTest;
-
-    if (!this.HavePlan)
-        Test.Builder.die("Can't change the current test number without a plan!");
-    this.CurrTest = num;
-    if (num > this.TestResults.length ) {
-        var reason = 'incrementing test number';
-        for (var i = this.TestResults.length; i < num; i++) {
-            this.TestResults[i] = {
-                ok:        true, 
-                actual_ok: null,
-                reason:    reason,
-                type:      'unknown', 
-                name:      null,
-                output:    'ok - ' + reason + Test.Builder.LF
-            };
-        }
-    } else if (num < this.TestResults.length) {
-        // IE requires the second argument to truncate the array.
-        this.TestResults.splice(num, this.TestResults.length);
-    }
-    return this.CurrTest;
-};
-
-Test.Builder.prototype.summary = function () {
-    var results = new Array(this.TestResults.length);
-    for (var i = 0; i < this.TestResults.length; i++) {
-        results[i] = this.TestResults[i]['ok'];
-    }
-    return results
-};
-
-Test.Builder.prototype.details = function () {
-    return this.TestResults;
-};
-
-Test.Builder.prototype.todo = function (why, howMany) {
-    if (howMany) this.ToDo = [why, howMany];
-    return this.ToDo[1];
-};
-
-Test.Builder.prototype._todo = function () {
-    if (this.ToDo[1]) {
-        if (this.ToDo[1]--) return this.ToDo[0];
-        this.ToDo = [];
-    }
-    return false;
-};
-
-Test.Builder.prototype._sanity_check = function () {
-    Test.Builder._whoa(
-        this.CurrTest < 0,
-        'Says here you ran a negative number of tests!'
-    );
-
-    Test.Builder._whoa(
-        !this.HavePlan && this.CurrTest, 
-        'Somehow your tests ran without a plan!'
-    );
-
-    Test.Builder._whoa(
-        this.CurrTest != this.TestResults.length,
-        'Somehow you got a different number of results than tests ran!'
-    );
-};
-
-Test.Builder.prototype._notifyHarness = function () {
-    var top = Test.Builder.globalScope;
-    // Special treatment for the browser harness.
-    if (top.parent && top.parent.Test && top.parent.Test.Harness) {
-        top.parent.Test.Harness.Done++;
-    }
-};
-
-Test.Builder.prototype._ending = function () {
-    if (this.Ended) return;
-    this.Ended = true;
-    if (this.noEnding()) {
-        this._notifyHarness();
-        return;
-    }
-    this._sanity_check();
-    var out = this.endOutput();
-
-    // Figure out if we passed or failed and print helpful messages.
-    if( this.TestResults.length ) {
-        // The plan?  We have no plan.
-        if (this.NoPlan) {
-            if (!this.noHeader())
-                this._print("1.." + this.CurrTest + Test.Builder.LF);
-            this.ExpectedTests = this.CurrTest;
-        }
-
-        var numFailed = 0;
-        for (var i = 0; i < this.TestResults.length; i++) {
-            if (!this.TestResults[i]) numFailed++;
-        }
-        numFailed += Math.abs(
-            this.ExpectedTests - this.TestResults.length
-        );
-
-        if (this.CurrTest < this.ExpectedTests) {
-            var s = this.ExpectedTests == 1 ? '' : 's';
-            out(
-                "# Looks like you planned " + this.ExpectedTests + " test"
-                + s + " but only ran " + this.CurrTest + "." + Test.Builder.LF
-            );
-        } else if (this.CurrTest > this.ExpectedTests) {
-           var numExtra = this.CurrTest - this.ExpectedTests;
-            var s = this.ExpectedTests == 1 ? '' : 's';
-            out(
-                "# Looks like you planned " + this.ExpectedTests + " test"
-                + s + " but ran " + numExtra + " extra." + Test.Builder.LF
-            );
-        } else if (numFailed) {
-            var s = numFailed == 1 ? '' : 's';
-            out(
-                "# Looks like you failed " + numFailed + "test" + s + " of "
-                + this.ExpectedTests + "." + Test.Builder.LF
-            );
-        }
-
-        if (this.TestDied) {
-            out(
-                "# Looks like your test died just after " 
-                + this.CurrTest + "." + Test.Builder.LF
-            );
-        }
-
-    } else if (!this.SkipAll) {
-        // skipAll requires no status output.
-        if (this.TestDied) {
-            out(
-                "# Looks like your test died before it could output anything."
-                + Test.Builder.LF
-            );
-        } else {
-            out("# No tests run!" + Test.Builder.LF);
-        }
-    }
-    this._notifyHarness();
-};
-
-Test.Builder.prototype.isUndef = function (got, op, expect, desc) {
-    // Undefined only matches undefined, so we don't need to cast anything.
-    var test = eval("got " + (Test.Builder.StringOps[op] || op) + " expect");
-    this.ok(test, desc);
-    if (!test) this._isDiag(got, op, expect);
-    return test;
-};
-
-Test.Builder._finish = function (pkg) {
-    if (!pkg) pkg = Test;
-    for (var i = 0; i < pkg.Builder.Instances.length; i++) {
-        // The main process is always async ID 0.
-        if (!pkg.Builder.Instances[i].Ended)
-            pkg.Builder.Instances[i].endAsync(0);
-    }
-};
-
-if (Test.Builder.globalScope) {
-    // Set up an onload function to end all tests.
-    Test.Builder.globalScope.onload = function (event, pkg) {
-        // The package may be passed in if onload() is called explicitly.
-        // This is to get around a very weird scoping bug in my version of
-        // Firefox. See Test.Harness.Browser.runTest() for this usage.
-        Test.Builder._finish(pkg)
-    };
-
-    // Set up an exception handler. This is so that we can capture deaths but
-    // still output information for TestHarness to pick up.
-    Test.Builder.globalScope.onerror = function (msg, url, line) {
-        // Output the exception.
-        Test.Builder.Test.TestDied = true;
-        Test.Builder.Test.diag("Error in " + url + " at line " + line + ": " + msg);
-        return true;
-    };
-};
-
-Test.Builder.prototype.beginAsync = function (timeout) {
-	var id = ++this.asyncID;
-    var top = Test.Builder.globalScope;
-    if (timeout && top && top.setTimeout) {
-        // Are there other ways of setting timeout in non-browser settings?
-        var aTest = this;
-        this.asyncs[id] = top.setTimeout(
-            function () { aTest.endAsync(id) }, timeout
-        );
-    } else {
-        // Make sure it's defined.
-        this.asyncs[id] = 0;
-    }
-	return id;
-};
-
-Test.Builder.prototype.endAsync = function (id) {
-    if (this.asyncs[id] == undefined) return;
-    if (this.asyncs[id]) {
-		// Remove the timeout
-		Test.Builder.globalScope.clearTimeout(this.asyncs[id]);
-	}
-    if (--this.asyncID < 0) this._ending();
-};
-
-Test.Builder.exporter = function (pkg, root) {
-    if (typeof root == 'undefined') {
-        root = Test.Builder.globalScope;
-        if (!root) throw new Error("Platform unknown");
-    }
-    for (var i = 0; i < pkg.EXPORT.length; i++) {
-        if (typeof root[pkg.EXPORT[i]] == 'undefined')
-            root[pkg.EXPORT[i]] = pkg[pkg.EXPORT[i]];
-    }
-};
@@ -1,363 +0,0 @@
-// # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
-
-if (typeof JSAN != 'undefined') JSAN.use('Test.Harness');
-else {
-    if (typeof Test == 'undefined') Test = {};
-    if (!Test.Harness) Test.Harness = {};
-}
-
-if (window.parent != window &&
-    location.href.replace(/[?#].+/, "") == parent.location.href.replace(/[?#].+/, ""))
-{
-
-    // Build fake T.H.B so original script from this file doesn't throw
-    // exception. This is a bit of a hack...
-    Test.Harness.Browser = function() {
-        this.runTests = function() {},
-        this.encoding = function () { return this }
-    };
-
-    // We're in a test iframe. Set up the necessary parts and load the
-    // test script with XMLHttpRequest (to support Safari and Opera).
-    var __MY = {};
-    __MY.pre = document.createElement("pre");
-    __MY.pre.id = "test";
-    if (window.parent.Test.Harness.Browser._encoding) {
-        // Set all scripts to use the appropriate encoding.
-        __MY.scripts = document.getElementsByTagName('script');
-        for (var j = 0; j < __MY.scripts.length; j++) {
-            __MY.scripts[j].charset =
-                window.parent.Test.Harness.Browser._encoding;
-        }
-    }
-
-    // XXX replace with a script element at some point? Safari is due to
-    // have this working soon (not sure about IE or Opera):
-    // http://bugzilla.opendarwin.org/show_bug.cgi?id=3748
-    __MY.inc = window.parent.Test.Harness.Browser.includes;
-    __MY.req = typeof XMLHttpRequest != 'undefined'
-      ? new XMLHttpRequest()
-      : new ActiveXObject("Microsoft.XMLHTTP");
-
-    for (var k = 0; k < __MY.inc.length; k++) {
-        __MY.req.open("GET", __MY.inc[k], false);
-        __MY.req.send(null);
-        var stat = __MY.req.status;
-        //           OK   Not Modified    IE Cached   Safari cached
-        if (stat == 200 || stat == 304 || stat == 0 || stat == null) {
-            eval(__MY.req.responseText);
-        } else {
-            throw new Error(
-                "Unable to load " + __MY.inc[k]
-                + ': Status ' + __MY.req.status
-            );
-        }
-    }
-
-    // IE 6 SP 2 doesn't seem to run the onload() event, so we force the
-    // issue.
-    Test.Builder._finish(Test);
-
-    // XXX Opera throws a DOM exception here, but I don't know what to do
-    // about that.
-    __MY.body = document.body
-        || document.getElementsByTagName("body")[0].appendChild(__MY.pre);
-    if (__MY.body) __MY.body.appendChild(__MY.pre);
-    else if (document.appendChild) document.appendChild(__MY.pre);
-
-} else {
-    Test.Harness.Browser = function () {
-        this.includes = Test.Harness.Browser.includes = [];
-        Array.prototype.push.apply(Test.Harness.Browser.includes, arguments);
-        this.includes.push('');
-    };
-
-    Test.Harness.Browser.VERSION = '0.21';
-
-    Test.Harness.Browser.runTests = function () {
-        var harness = new Test.Harness.Browser();
-        harness.runTests.apply(harness, arguments);
-    };
-
-    Test.Harness.Browser.prototype = new Test.Harness();
-    Test.Harness.Browser.prototype.interval = 100;
-
-    Test.Harness.Browser.prototype._setupFrame = function () {
-        // Setup the iFrame to run the tests.
-        var node = document.getElementById('buffer');
-        if (node) return node.contentWindow || frames.buffer;
-        node = document.createElement("iframe");
-        node.setAttribute("id", "buffer");
-        node.setAttribute("name", "buffer");
-        // Safari makes it impossible to do anything with the iframe if it's
-        // set to display:none. See:
-        // http://www.quirksmode.org/bugreports/archives/2005/02/hidden_iframes.html
-        //alert(navigator.userAgent);
-        if (/Safari|Konqueror/.test(navigator.userAgent)) {
-            node.style.visibility = "hidden";
-            node.style.height = "0"; 
-            node.style.width = "0";
-        } else
-            node.style.display = "none";
-        document.body.appendChild(node);
-        return node.contentWindow || frames.buffer;
-    };
-
-    Test.Harness.Browser.prototype._setupOutput = function () {
-        // Setup the pre element for test output.
-        var node = document.createElement("pre");
-        node.setAttribute("id", "output");
-        document.body.appendChild(node);
-        // Brutal hack to make output linkable
-        fixoutput = function(node) {
-            // Trailing space added and replaced to work around yet another
-            // Safari bug.
-            node.innerHTML = node.innerHTML.replace(
-                / ?(\w[\w\.]+?\w)(?=\.\.\.)/m, '<a href="$1">$1</a>'
-            ) + ' ';
-        };
-        return {
-            pass: function (msg) {
-                node.appendChild(document.createTextNode(msg));
-                window.scrollTo(0, document.body.offsetHeight
-                                || document.body.scrollHeight);
-                fixoutput(node);
-            },
-            fail: function (msg) {
-                var red = document.createElement("span");
-                red.setAttribute("style", "color: red; font-weight: bold");
-                node.appendChild(red);
-                red.appendChild(document.createTextNode(msg));
-                window.scrollTo(0, document.body.offsetHeight
-                                || document.body.scrollHeight);
-            }
-        };
-    };
-
-    Test.Harness.Browser.prototype._setupSummary = function () {
-        // Setup the div for the summary.
-        var node = document.createElement("div");
-        node.setAttribute("id", "summary");
-        node.setAttribute(
-            "style", "white-space:pre; font-family: Verdana,Arial,serif;"
-        );
-        document.body.appendChild(node);
-        return function (msg) {
-            node.appendChild(document.createTextNode(msg));
-            window.scrollTo(0, document.body.offsetHeight
-                            || document.body.scrollHeight);
-        };
-};
-
-    Test.Harness.Browser.prototype.runTests = function () {
-        Test.Harness.Browser._encoding = this.encoding();
-        var files = this.args.file
-        ? typeof this.args.file == 'string' ? [this.args.file] : this.args.file
-        : arguments;
-        if (!files.length) return;
-        var outfiles = this.outFileNames(files);
-        var buffer = this._setupFrame();
-        var harness = this;
-        var ti = 0;
-        var start;
-        var output = this._setupOutput();
-        var summaryOutput = this._setupSummary();
-        // These depend on how we're watching for a test to finish.
-        var finish = function () {}, runNext = function () {};
-
-        // This function handles most of the work of outputting results and
-        // running the next test, if there is one.
-        var runner = function () {
-            harness.outputResults(
-                buffer.Test.Builder.Test,
-                files[ti],
-                output,
-                harness.args
-            );
-
-            if (files[++ti]) {
-                output.pass(
-                    outfiles[ti]
-                    + (harness.args.verbose ? Test.Harness.LF : '')
-                );
-                harness.runTest(files[ti], buffer);
-                runNext();
-            } else {
-                harness.outputSummary(summaryOutput, new Date() - start);
-                finish();
-            }
-        };
-
-        if (Object.watch) {
-            // We can use the cool watch method, and avoid setting timeouts!
-            // We just need to unwatch() when all tests are finished.
-            finish = function () { Test.Harness.unwatch('Done') };
-            Test.Harness.watch('Done', function (attr, prev, next) {
-                if (next < buffer.Test.Builder.Instances.length) return next;
-                runner();
-                return 0;
-            });
-        } else {
-            // Damn. We have to set timeouts. :-(
-            var pkg;
-            var wait = function () {
-                // Check Test.Harness.Done. If it's non-zero, then we know
-                // that the buffer is fully loaded, because it has incremented
-                // Test.Harness.Done. Grrr.. IE 6 SP 2 seems to delete
-                // buffer.Test after all the tests have finished running, but
-                // before this code executes for the correct number of
-                // completed tests. So we cache it in a variable outside of
-                // the function on previous calls to the function.
-                if (!pkg) pkg = buffer.Test;
-                if (Test.Harness.Done > 0
-                    && Test.Harness.Done >= pkg.Builder.Instances.length)
-                {
-                    Test.Harness.Done = 0;
-                    // Avoid race condition by resetting the instances, too. I
-                    // have no idea why this might remain set from a previous
-                    // test, but such can be the case in IE 6 SP 2.
-                    pkg.Builder.Instances = [];
-                    runner();
-                } else {
-                    window.setTimeout(wait, harness.interval);
-                }
-            };
-            // We'll just have to set a timeout for the next test.
-            runNext = function () {
-                window.setTimeout(wait, harness.interval);
-            };
-            window.setTimeout(wait, this.interval);
-        }
-
-        // Now start the first test.
-        output.pass(outfiles[ti] + (this.args.verbose ? Test.Harness.LF : ''));
-        start = new Date();
-        this.runTest(files[ti], buffer);
-    };
-
-    Test.Harness.Browser.prototype.runTest = function (file, buffer) {
-        if (/\.html$/.test(file)) {
-            buffer.location.replace(file);
-        } else { // if (/\.js$/.test(file)) {
-            if (/MSIE/.test(navigator.userAgent)
-                || /Opera/.test(navigator.userAgent)
-                || /Safari|Konqueror/.test(navigator.userAgent))
-            {
-                // These browsers have problems with the DOM solution. It
-                // simply doesn't work in Safari, and Opera considers its
-                // handling of buffer.document to be a security violation. So
-                // have them use the XML hack, instead.
-                this.includes[this.includes.length-1] = file;
-                buffer.location.replace(location.pathname + "?xml-hack=1");
-                return;
-            }
-            // document.write() simply doesn't work here. Thanks to
-            // Pawel Chmielowski for figuring that out!
-            var doc = buffer.document;
-            doc.open("text/html");
-            doc.close();
-            var el;
-
-            // XXX Opera chokes on this line. It thinks that using the doc
-            // element like this is a security violation, never mind that we
-            // were the ones who actually created it. Whatever!
-            var body = doc.body || doc.getElementsByTagName("body")[0];
-            var head = doc.getElementsByTagName("head")[0];
-
-            // Safari seems to be headless at this point.
-            if (!head) {
-                head = doc.createElement('head');
-                doc.appendChild(head);
-            }
-
-            // Add script elements for all includes.
-            for (var i = 0; i < this.includes.length - 1; i++) {
-                el = doc.createElement("script");
-                el.setAttribute("src", this.includes[i]);
-                head.appendChild(el);
-            }
-
-
-            // Create the pre and script element for the test file.
-            var pre = doc.createElement("pre");
-            pre.id = "test";
-            el = doc.createElement("script");
-            el.type = "text/javascript";
-            if (this.encoding()) el.charset = this.encoding();
-
-            // XXX This doesn't work in Safari right now. See
-            // http://bugzilla.opendarwin.org/show_bug.cgi?id=3748
-            el.src = file;
-            pre.appendChild(el);
-
-            // Create a script element to finish the tests.
-            el = doc.createElement("script");
-            el.type = "text/javascript";
-            var text = "window.onload(null, Test)";
-
-            // IE doesn't let script elements have children.
-            if (null != el.canHaveChildren) el.text = text;
-            // But most other browsers do.
-            else el.appendChild(document.createTextNode(text));
-
-            pre.appendChild(el);
-
-            // IE 6 SP 2 Requires getting the body element again.
-            body = doc.body || doc.getElementsByTagName("body")[0];
-            body.appendChild(pre);
-        /* Let's just assume that if it's not .html, it's JavaScript.
-        } else {
-            // Who are you, man??
-            alert("I don't know what kind of file '" + file + "' is");
-        */
-        }
-    };
-
-    Test.Harness.Browser.prototype.args = {};
-    var pairs = location.search.substring(1).split(/[;&]/);
-    for (var i = 0; i < pairs.length; i++) {
-        var parts = pairs[i].split('=');
-        if (parts[0] == null) continue;
-        var key = unescape(parts[0]), val = unescape(parts[1]);
-        if (Test.Harness.Browser.prototype.args[key] == null) {
-            Test.Harness.Browser.prototype.args[key] = unescape(val);
-        } else {
-            if (typeof Test.Harness.Browser.prototype.args[key] == 'string') {
-                Test.Harness.Browser.prototype.args[key] =
-                    [Test.Harness.Browser.prototype.args[key], unescape(val)];
-            } else {
-                Test.Harness.Browser.prototype.args[key].push(unescape(val));
-            }
-        }
-    }
-    delete pairs;
-
-    Test.Harness.Browser.prototype.formatFailures = function (fn) {
-        // XXX Switch to DOM?
-        var failedStr = "Failed Test";
-        var middleStr = " Total Fail  Failed  ";
-        var listStr = "List of Failed";
-        var table =
-            '<style>table {padding: 0; border-collapse: collapse; }'
-          + 'tr { height: 2em; }'
-          + 'th { background: lightgrey; }'
-          + 'td, th { padding: 2px 5px; text-align: left; border: solid #000000 1px;}'
-          + '.odd { background: #e8e8cd }'
-          + '</style>'
-          + '<table style="padding: 0"><tr><th>Failed Test</th><th>Total</th>'
-          + '<th>Fail</th><th>Failed</th></tr>';
-        for (var i = 0; i < this.failures.length; i++) {
-            var track = this.failures[i];
-            var style = i % 2 ? 'even' : 'odd';
-            table += '<tr class="' + style + '"><td>' + track.fn + '</td>'
-              + '<td>' + track.total + '</td>'
-              + '<td>' + (track.total - track.ok) + '</td>'
-              + '<td>' + this._failList(track.failList) + '</td></tr>';
-        };
-        table += '</table>' + Test.Harness.LF;
-        var node = document.getElementById('summary');
-        node.innerHTML += table;
-        window.scrollTo(0, document.body.offsetHeight
-                        || document.body.scrollHeight);
-    };
-}
@@ -1,277 +0,0 @@
-// # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
-
-// Set up namespace.
-if (typeof self != 'undefined') {
-    //Browser
-    if (typeof Test == 'undefined') Test = {PLATFORM: 'browser'};
-    else Test.PLATFORM = 'browser';
-} else if (typeof _player != 'undefined'){
-    //Director
-    if (typeof _global.Test != "object") _global.Test = {PLATFORM: 'director'};
-    else _global.Test.PLATFORM = 'director';
-} else {
-    throw new Error("Test.Harness does not support your platform");
-}
-
-Test.Harness = function () {};
-Test.Harness.VERSION = '0.21';
-Test.Harness.Done = 0;
-
-// Stoopid IE.
-Test.Harness.LF = typeof navigator != "undefined"
-    && navigator.userAgent.toLowerCase().indexOf('msie') + 1
-    && ( ( typeof JSAN != "undefined" && JSAN.globalScope.opera == undefined )
-         || ( Test.PLATFORM == 'browser' && window.opera == undefined ) )
-  ? "\r"
-  : "\n";
-
-Test.Harness.prototype.isDone = Test.Harness.isDone;
-
-/*
-
-    bonus           Number of individual todo tests unexpectedly passed
-    ran             Number of individual tests ran
-    ok              Number of individual tests passed
-    subSkipped      Number of individual tests skipped
-    todo            Number of individual todo tests
-
-    files           Number of test files ran
-    good            Number of test files passed
-    bad             Number of test files failed
-    tests           Number of test files originally given
-    skipped         Number of test files skipped
-
-*/
-
-Test.Harness.prototype.bonus      = 0;
-Test.Harness.prototype.ran        = 0;
-Test.Harness.prototype.ok         = 0;
-Test.Harness.prototype.subSkipped = 0;
-Test.Harness.prototype.todo       = 0;
-Test.Harness.prototype.files      = 0;
-Test.Harness.prototype.good       = 0;
-Test.Harness.prototype.bad        = 0;
-Test.Harness.prototype.tests      = 0;
-Test.Harness.prototype.skipped    = 0;
-Test.Harness.prototype.failures   = [];
-Test.Harness.prototype._encoding  = null;
-
-Test.Harness.prototype.encoding = function (enc) {
-    if (!enc) return this._encoding;
-    this._encoding = enc;
-    return this;
-};
-
-Test.Harness.runTests = function () {
-    // XXX Can't handle inheritance, right? Or can we?
-    var harness = new Test.Harness();
-    harness.runTests.apply(harness, arguments);
-};
-
-Test.Harness.prototype.outFileNames = function (files) {
-    var len = 0;
-    for (var i = 0; i < files.length; i++) {
-        if (files[i].length > len) len = files[i].length;
-    }
-    len += 3;
-    var ret = [];
-    for (var i = 0; i < files.length; i++) {
-        var outName = files[i];
-        var add = len - files[i].length;
-        // Where is Perl's x operator when I need it??
-        for (var j = 0; j < add; j++) {
-            outName += '.';
-        }
-        ret.push(outName);
-    }
-    return ret;
-};
-
-Test.Harness.prototype.outputResults = function (test, file, out, attrs) {
-    this.tests++;
-    this.ran += test.TestResults.length;
-    var fails = [];
-    var track = {
-        fn:       file,
-        total:    test.expectedTests(),
-        ok:       0,
-        failList: []
-    };
-
-    if (test.TestResults.length) {
-        this.files++;
-        var pass = true;
-        for (var i = 0; i < test.TestResults.length; i++) {
-            // Start out assuming passage.
-            if (test.TestResults[i].ok) {
-                if (attrs.verbose) out.pass(test.TestResults[i].output);
-                this.ok++;
-                track.ok++
-                if (test.TestResults[i].type == 'todo') {
-                    // Handle unexpected pass.
-                    if (test.TestResults[i].actualOK) this.bonus++;
-                    this.todo ++;
-                } else if (test.TestResults[i].type == 'skip') this.subSkipped++;
-            } else {
-                if (test.TestResults[i].type == 'todo') {
-                    // Expected failure.
-                    this.todo++;
-                    if (attrs.verbose) out.pass(test.TestResults[i].output);
-                } else {
-                    pass = false;
-                    track.failList.push(i + 1);
-                    out.fail(test.TestResults[i].output);
-                }
-            }
-            
-        }
-        
-        if (pass) {
-            this.good++;
-            out.pass("ok" + Test.Harness.LF);
-        } else {
-            this.bad++;
-            var err = "NOK # Failed ";
-            if (track.failList.length == 1) {
-                err += "test " + track.failList[0];
-            } else {
-                err += "tests " + this._failList(track.failList);
-            }
-            out.fail(err + " in " + file + Test.Harness.LF);
-        }
-    } else if (test.SkipAll){
-        // All tests skipped.
-        this.skipped++;
-        this.good++;
-        out.pass(test.Buffer.join('').replace(/[^#]+#\s+Skip /, 'all skipped: '));
-    } else {
-        // Wha happened? Tests ran, but no results!
-        this.files++;
-        this.bad++;
-        out.fail("FAILED before any test output arrived" + Test.Harness.LF);
-    }
-    if (track.failList.length) this.failures.push(track);
-};
-
-Test.Harness.prototype._allOK = function () {
-    return this.bad == 0 && (this.ran || this.skipped) ? true : false;
-};
-
-Test.Harness.prototype.outputSummary = function (fn, time) {
-    var bonusmsg = this._bonusmsg();
-    var pct;
-    if (this._allOK()) {
-        fn("All tests successful" + bonusmsg + '.' + Test.Harness.LF);
-    } else if (!this.tests) {
-        fn("FAILED—no tests were run for some reason." + Test.Harness.LF);
-    } else if (!this.ran) {
-        var blurb = this.tests == 1 ? "file" : "files";
-        fn("FAILED—" + this.tests + " test " + blurb + " could be run, "
-           + "alas—no output ever seen." + Test.Harness.LF);
-    } else {
-        pct = this.good / this.tests * 100;
-        var pctOK = 100 * this.ok / this.ran;
-        var subpct = (this.ran - this.ok) + "/" + this.ran
-          + " subtests failed, " + pctOK.toPrecision(4) + "% okay.";
-
-        if (this.bad) {
-            bonusmsg = bonusmsg.replace(/^,?\s*/, '');
-            if (bonusmsg) fn(bonusmsg + '.' + Test.Harness.LF);
-            fn("Failed " + this.bad + "/" + this.tests + " test scripts, "
-               + pct.toPrecision(4) + "% okay. " + subpct + Test.Harness.LF);
-        }
-        this.formatFailures(fn);
-    }
-
-    fn("Files=" + this.tests + ", Tests=" + this.ran + ", " + (time / 1000)
-       + " seconds" + Test.Harness.LF);
-};
-
-Test.Harness.prototype.formatFailures = function () {
-    var table = '';
-    var failedStr = "Failed Test";
-    var middleStr = " Total Fail  Failed  ";
-    var listStr = "List of Failed";
-    var cols = 80;
-
-    // Figure out our longest name string for formatting purposes.
-    var maxNamelen = failedStr.length;
-    for (var i = 0; i < this.failures.length; i++) {
-        var len = this.failures[i].length;
-        if (len > maxNamelen) maxNamelen = len;
-    }
-
-    var listLen = cols - middleStr.length - maxNamelen.length;
-    if (listLen < listStr.length) {
-        listLen = listStr.length;
-        maxNamelen = cols - middleStr.length - listLen;
-        if (maxNamelen < failedStr.length) {
-            maxNamelen = failedStr.length;
-            cols = maxNamelen + middleStr.length + listLen;
-        }
-    }
-
-    var out = failedStr;
-    if (out.length < maxNamelen) {
-        for (var j = out.length; j < maxNameLength; j++) {
-            out += ' ';
-        }
-    }
-    out += '  ' + middleStr;
-    // XXX Need to finish implementing the text-only version of the failures
-    // table.
-};
-
-Test.Harness.prototype._bonusmsg = function () {
-    var bonusmsg = '';
-    if (this.bonus) {
-        bonusmsg = (" (" + this.bonus + " subtest" + (this.bonus > 1 ? 's' : '')
-          + " UNEXPECTEDLY SUCCEEDED)");
-    }
-
-    if (this.skipped) {
-        bonusmsg += ", " + this.skipped + " test"
-          + (this.skipped != 1 ? 's' : '');
-        if (this.subSkipped) {
-            bonusmsg += " and " + this.subSkipped + " subtest"
-              + (this.subSkipped != 1 ? 's' : '');
-        }
-        bonusmsg += ' skipped';
-    } else if (this.subSkipped) {
-        bonusmsg += ", " + this.subSkipped + " subtest"
-          + (this.subSkipped != 1 ? 's' : '') + " skipped";
-    }
-
-    return bonusmsg;
-}
-
-Test.Harness.prototype._failList = function (fails) {
-    var last = -1;
-    var dash = '';
-    var list = [];
-    for (var i = 0; i < fails.length; i++) {
-        if (dash) {
-            // We're in a series of numbers.
-            if (fails[i] - 1 == last) {
-                // We're still in it.
-                last = fails[i];
-            } else {
-                // End of the line.
-                list[list.length-1] += dash + last;
-                last = -1;
-                list.push(fails[i]);
-                dash = '';
-            }
-        } else if (fails[i] - 1 == last) {
-            // We're in a new series.
-            last = fails[i];
-            dash = '-';
-        } else {
-            // Not in a sequence.
-            list.push(fails[i]);
-            last = fails[i];
-        }
-    }
-    if (dash) list[list.length-1] += dash + last;
-    return list.join(' ');
-}
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../list.t.js';
-    </script>
-    <script type="text/javascript" src="../list.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../localise.t.js';
-    </script>
-    <script type="text/javascript" src="../localise.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../loop.t.js';
-    </script>
-    <script type="text/javascript" src="../loop.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../objects.t.js';
-    </script>
-    <script type="text/javascript" src="../objects.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../plugins.t.js';
-    </script>
-    <script type="text/javascript" src="../plugins.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../set.t.js';
-    </script>
-    <script type="text/javascript" src="../set.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../stash.t.js';
-    </script>
-    <script type="text/javascript" src="../stash.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,25 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-    <script type="text/javascript" src="../Test/Jemplate.js"></script>
-    <script type="text/javascript" src="JemplateRuntime.js"></script>
-    <script type="text/javascript" src="jemplate.js"></script>
-    <script type="text/javascript" src="jemplate2.js"></script>
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '../string.t.js';
-    </script>
-    <script type="text/javascript" src="../string.t.js"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,16 +0,0 @@
-<html>
-<head>
-    <title>[% index_title %]</title>
-    <script type="text/javascript" src="lib/Test/Harness.js"></script>
-    <script type="text/javascript" src="lib/Test/Harness/Browser.js"></script>
-</head>
-<body>
-<script type="text/javascript">
-    var t = new Test.Harness.Browser();
-    
-    t.runTests(
-        "[% all_test_files.join("\",\n        \"") %]"
-    );
-</script>
-</body>
-</html>
@@ -1,24 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="lib/Subclass.js"></script>
-    <script type="text/javascript" src="lib/Test/Builder.js"></script>
-    <script type="text/javascript" src="lib/Test/Base.js"></script>
-[% FOR lib = import_libs -%]
-    <script type="text/javascript" src="[% lib %]"></script>
-[% END -%]
-    <script type="text/javascript" src="jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '[% test_file %]';
-    </script>
-    <script type="text/javascript" src="[% test_file %]"></script>
-</head>
-<body>
-</body>
-</html>
@@ -1,23 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Setting a value into a hash works
---- jemplate
-test.html
-[% testhash = {} -%]
-[% testkey = 'bob' -%]
-[% testhash.$testkey = 'bozo' -%]
-Value: [% testhash.$testkey %]<br />
---- output
-Value: bozo<br />
-
-*/
@@ -1,52 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate',
-    raw_context: 'raw_context'
-};
-
-t.plan(3);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Both .keys and .keys() works
---- context
-{"hash":{"foo":"FOO","bar":"BAR"}}
---- jemplate
-stash-functions1.html
-[% hash.keys.sort.join('+') %]
-[% hash.keys().sort().join('+') %]
---- output
-bar+foo
-bar+foo
-
-=== Disambiguatation of .keys and .keys()
---- context
-{"hash":{"keys":"foo","values":"bar"}}
---- jemplate
-stash-functions2.html
-[% hash.keys %]
-[% hash.keys().join('+') %]
---- output
-foo
-keys+values
-
-=== Stashed Function
---- raw_context
-{ hash: { 
-    noarg: function() { return "noarg" },
-    arg:   function(arg) { return "arg: " + arg }
-} }
---- jemplate
-stash-functions3.html
-[% hash.noarg %]
-[% hash.noarg() %]
-[% hash.arg("abc") %]
---- output
-noarg
-noarg
-arg: abc
-
-*/
@@ -1,132 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(10);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test chunk method
---- jemplate
-string_chunk.html
-[% SET a = '1234567890' -%]
-[% a.chunk.join(' ') %]
-[% a.chunk(2).join(' ') %]
-[% a.chunk(3).join(' ') %]
-[% a.chunk(9).join(' ') %]
-[% a.chunk(10).join(' ') %]
-[% a.chunk(-1).join(' ') %]
-[% a.chunk(-2).join(' ') %]
-[% a.chunk(-3).join(' ') %]
-[% a.chunk(-9).join(' ') %]
-[% a.chunk(-10).join(' ') %]
---- output
-1 2 3 4 5 6 7 8 9 0
-12 34 56 78 90
-123 456 789 0
-123456789 0
-1234567890
-1 2 3 4 5 6 7 8 9 0
-12 34 56 78 90
-1 234 567 890
-1 234567890
-1234567890
-
-=== Test defined method
---- jemplate
-string_defined.html
-[% SET a = '1' -%]
-[% a.defined ? '1' : '0' %]
---- output
-1
-
-=== Test hash method
---- jemplate
-string_hash.html
-[% SET a = 'Hi' -%]
-[% a.hash.value %]
---- output
-Hi
-
-=== Test list method
---- jemplate
-string_list.html
-[% SET a = 'Hi' -%]
-[% a.list.0 %]
---- output
-Hi
-
-=== Test match method
---- jemplate
-string_match.html
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.match('\\\d\\\d').join(' ') %]
---- output
-12 34 56
-
-=== Test repeat method
---- jemplate
-string_repeat.html
-[% SET a = 'aaa' -%]
-[% a.repeat(3) %]
-[% a.repeat() %]
---- output
-aaaaaaaaa
-aaa
-
-=== Test replace method
---- jemplate
-string_replace.html
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.replace('\\\d\\\d', 'bb') %]
-[% a.replace('\\\d\\\d') %]
---- output
-aaabbaaabbaaabb
-aaaaaaaaa
-
-=== Test search method
---- jemplate
-string_search.html
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.search('\\\d\\\d') ? 1 : 0 %]
-[% a.search('w') ? 1 : 0 %]
---- output
-1
-0
-
-=== Test size method
---- jemplate
-string_size.html
-[% SET a = '1' -%]
-[% a.defined ? '1' : '0' %]
---- output
-1
-
-=== Test split method
---- jemplate
-string_split.html
-[% SET a = 'aaa12aaa34aaa' -%]
-[% a.split('\\\d\\\d').join(' ') %]
-[% SET a = '1aaa2aaa3aaa4' -%]
-[% a.split('aaa').join(' ') %]
---- output
-aaa aaa aaa
-1 2 3 4
-
-*/
-
-/*
-=== Test length method
---- jemplate
-string_length.html
-[% SET a = 'Hi' -%]
-[% a.length %]
-[% SET a = 10 -%]
-[% a.length %]
---- output
-2
-
-*/
@@ -0,0 +1,76 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate'
+};
+
+t.plan(6);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Basic Substitution1
+--- context
+{"name":"Wally"}
+--- jemplate
+hello.html
+Hello, [% name %]
+--- output
+Hello, Wally
+
+=== Basic Substitution2
+--- context
+{"name":"Yann"}
+--- jemplate: hello.html
+--- output
+Hello, Yann
+
+=== Operator "=="
+--- jemplate
+operator1.html
+[%- IF "abc" == "abc" -%]
+same
+[%- END %]
+--- output
+same
+
+=== Operator "!="
+--- jemplate
+operator2.html
+[%- IF "abc" != "def" -%]
+not same
+[%- END -%]
+
+--- output
+not same
+
+=== Operator concat "+"
+--- jemplate
+operator3.html
+[%- "abc" _ "def" -%]
+
+--- output
+abcdef
+
+=== Array index fetch"
+--- jemplate
+basic_array1.html
+[%- JAVASCRIPT -%]
+stash.set( "simple_list", ["a","b","c"] );
+stash.set( "mylist", [["a","b","c"],["d","e","f"],["h","i","j"]] );
+[%- END -%]
+a = [% simple_list.0 %]
+a = [% mylist.0.0 %]
+c = [% mylist.0.2 %]
+e = [% mylist.1.1 %]
+j = [% mylist.2.2 -%]
+
+--- output
+a = a
+a = a
+c = c
+e = e
+j = j
+
+*/
@@ -0,0 +1,56 @@
+var t = new Test.Jemplate();
+t.plan(1);
+t.pass('XXX - Finish writing this test file');
+
+/* Test
+=== Foo
+--- xxx
+yyy
+
+*/
+// NODE CLASS
+
+/*
+=item * val = getName()
+
+This returns the name assigned to this object by the serverside code.
+
+If no name has been set, then this will return "E<lt>no nameE<gt>".
+
+*/
+
+/*
+,getName: function () { return this.name ? this.name : '<no name>'; } */
+
+/*
+
+-----------------------------------------
+// JEMPLATE CALL
+
+while(!iterator.atEnd()) {
+  var node = iterator.get();
+  tableBody += Jemplate.process('nl_table_row.html', { node: node, mode: this.mode });
+}
+this.viewer.innerHTML += tableBody;
+
+------------------------------------------
+// TEMPLATE
+
+<tr>
+  <td>
+    <a href="" id="node-[% node.id %]-list-item">
+    [% node.getName() %] - [% node.name %]
+    </a>
+  </td>
+  [% IF mode == 'updated' %]
+  <td id="node-[% node.id %]-list-timestamp">
+    [% node.updated_on_formatted %]
+  </td>
+  [% END %]
+</tr>
+
+-------------------------------------------
+
+// node.getName() outputs nothing, but node.name outputs correctly. the getName() method definitely works.
+
+*/
@@ -0,0 +1,24 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate'
+};
+
+t.plan(1);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== DEFAULT directive works
+--- context
+{"foo":"one"}
+--- jemplate
+default.html
+[% DEFAULT foo = 'two', bar = 'three' -%]
+[% DEFAULT bar = 'four' -%]
+[% foo %] | [% bar %]
+--- output
+one | three
+
+*/
@@ -0,0 +1,74 @@
+
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(5);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test BLOCK/SET/FOR/PROCESS
+--- jemplate
+directives1.html
+[% BLOCK foo -%]
+I <3 Sushi
+[% END -%]
+[% SET list = [3, 4, 5] -%]
+[% FOR i = list -%]
+[% PROCESS foo -%]
+[% END -%]
+--- output
+I <3 Sushi
+I <3 Sushi
+I <3 Sushi
+
+=== Test WHILE/IF/ELSE
+--- jemplate
+directives2.html
+[% SET num = 4 -%]
+[% array = [] -%]
+[% WHILE num < 7 -%]
+[% IF num % 2 %][% CALL array.push('Odd') %][% ELSE %][% CALL array.push('Even') %][% END -%]
+[% num = num + 1 -%]
+[% END -%]
+[% array.join('***') %]
+--- output
+Even***Odd***Even
+
+=== Test JAVASCRIPT
+--- jemplate
+directives3.html
+[%- JAVASCRIPT -%]
+stash.set("obj", {"key1": "val1", "key2": "val2"});
+[%- END -%]
+Key1: [% obj.key1 %]
+Key2: [% obj.key2 %]
+--- output
+Key1: val1
+Key2: val2
+
+=== Test FOR i IN obj
+--- jemplate
+directives4.html
+[%- JAVASCRIPT -%]
+stash.set("obj", {"key1": "val1", "key2": "val2"});
+[%- END -%]
+[%- FOR key IN obj -%]
+[% key %]: [% obj.$key %]
+[% END -%]
+--- output
+key1: val1
+key2: val2
+=== Test NEXT
+--- jemplate
+directives5.html
+[%- FOR i IN [1,2,3,4,5,6] -%]
+[%- NEXT IF i % 2 == 0 -%]
+I = [% i %];
+[%- END -%]
+--- output
+I = 1;I = 3;I = 5;
+*/
@@ -0,0 +1,167 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+/*
+#     Failed test
+#          got: "my%20file.html
+# my%3Cfile%20%26%20your%3Efile.html
+# my%3Cfile%20%26%20your%3Efile.html
+# guitar%26amp%3Bfile.html
+# guitar%26amp%3Bfile.html
+# "
+#     expected: "my%20file.html
+# my%3Cfile%20&amp;%20your%3Efile.html
+# my%3Cfile%20&amp;amp;%20your%3Efile.html
+# guitar&amp;amp;file.html
+# guitar&amp;amp;amp;file.html
+# "
+
+my%20file.html
+my%3Cfile%20&%20your%3Efile.html
+my%3Cfile%20&amp;%20your%3Efile.html
+guitar&amp;file.html
+guitar&amp;amp;file.html
+
+*/
+
+t.plan(7);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test indent
+--- jemplate
+filters_indent.html
+[% FILTER indent -%]
+1
+2
+3
+4
+[%- END %]
+#
+[% FILTER indent(3) -%]
+1
+2
+3
+4
+[%- END %]
+#
+[% FILTER indent('2') -%]
+1
+2
+3
+4
+[%- END %]
+#
+[% FILTER indent(0) -%]
+1
+2
+3
+4
+[%- END %]
+#
+[% text = 'The cat sat on the mat';
+   text | indent('> ') | indent('+') %]
+--- output
+    1
+    2
+    3
+    4
+#
+   1
+   2
+   3
+   4
+#
+  1
+  2
+  3
+  4
+#
+1
+2
+3
+4
+#
++> The cat sat on the mat
+=== Test truncate
+--- jemplate
+filters_truncate.html
+[% a = '1234567890' -%]
+[% a | truncate(5)  %]
+[% a | truncate(10) %]
+[% a | truncate(15) %]
+[% a | truncate(2)  %]
+[% a = '1234567890123456789012345678901234567890' -%]
+[% a | truncate  %]
+--- output
+12...
+1234567...
+1234567890
+...
+12345678901234567890123456789...
+
+=== Test null
+--- jemplate
+filters_null.html
+[%- "Ils ont les chapeaux ronds, vive la bretagne" | null -%]
+--- output
+=== Test uri
+--- jemplate
+filters_uri.html
+[% "my file.html" FILTER uri %]
+[% "my<file & your>file.html" FILTER uri %]
+[% "my<file & your>file.html" | uri | html %]
+[% "guitar&amp;file.html" | uri %]
+[% "guitar&amp;file.html" | uri | html %]
+--- output
+my%20file.html
+my%3Cfile%20%26%20your%3Efile.html
+my%3Cfile%20%26%20your%3Efile.html
+guitar%26amp%3Bfile.html
+guitar%26amp%3Bfile.html
+=== Test html 
+--- jemplate
+filters_html.html
+[% FILTER html %]This is some html text
+All the <tags> should be escaped & protected
+[% END %]
+[% text = "The <cat> sat on the <mat>" %]
+[% text FILTER html %]
+[% FILTER html %]
+"It isn't what I expected", he replied.[% END %]
+--- output
+This is some html text
+All the &lt;tags&gt; should be escaped &amp; protected
+
+
+The &lt;cat&gt; sat on the &lt;mat&gt;
+
+&quot;It isn't what I expected&quot;, he replied.
+
+=== Test repeat
+--- jemplate
+filters_repeat.html
+[% "foo..." FILTER repeat(5) %]
+--- output
+foo...foo...foo...foo...foo...
+
+=== Test replace
+--- jemplate
+filters_replace.html
+[%- text = "The cat sat on the mat" %]
+[%- text FILTER replace(' ', '_') %]
+[% text FILTER replace('sat', 'shat') +%]
+[% text | replace('at', 'plat') +%]
+[% text = 'The <=> operator, blah, blah' %]
+[%- text | html | replace('blah', 'rhubarb') %]
+--- output
+The_cat_sat_on_the_mat
+The cat shat on the mat
+The cplat splat on the mplat
+The &lt;=&gt; operator, rhubarb, rhubarb
+
+*/
@@ -0,0 +1,23 @@
+var t = new Test.Jemplate();
+
+t.plan(1);
+
+t.compile();
+
+var j = new Jemplate();
+
+var test1 = t.state.blocks.shift();
+t.is(
+    j.process('greetings', {name: 'Jesus'}),
+    test1.data.output,
+    test1.name
+);
+
+/* Test
+=== Include files with path names.
+--- output
+English: Howdy Jesus. How are you?
+
+Spanish: Hola Jesus. Como estas?
+
+*/
@@ -0,0 +1,119 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(9);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test each method
+--- jemplate
+hash_each.html
+[%# This is a bit convoluted because sort order can not be guaranteed -%]
+[%# please fix it if you can find a better way... -%]
+[%# There are a few other tests below that are very similar -%]
+[% SET hash = { a=1 b=2 c=3 };
+   SET list = hash.each;
+   FOR kindex IN [0,2,4];
+     SET vindex = kindex + 1;
+     SET key = list.${kindex};
+     SET value = list.${vindex};
+     (key AND hash.${key} == value) ? 1 : 0;
+   END %]
+--- output
+111
+
+=== Test exists method
+--- jemplate
+hash_exists.html
+[% SET a = { a=1 b=2 c=3 } -%]
+[% a.exists('b') ? 1 : 0 %]
+[% a.exists('z') ? 1 : 0 %]
+--- output
+1
+0
+
+=== Test import method
+--- jemplate
+hash_import.html
+[% SET a = { a=1 b=2 c=3 } -%]
+[% CALL a.import({ a=2 b=1 d=4 e=5 }) -%]
+[% a.exists('c') ? 1 : 0 %]
+[% a.exists('d') ? 1 : 0 %]
+[% a.a %]
+[% a.e %]
+--- output
+1
+1
+2
+5
+
+=== Test keys method
+--- jemplate
+hash_keys.html
+[% SET a = { a=1 b=2 c=3 } -%]
+[% a.keys.sort.join(' ') %]
+--- output
+a b c
+
+=== Test list method
+--- jemplate
+hash_list.html
+[% SET hash = { a=1 b=2 c=3 } -%]
+[% hash.list('keys').sort.join(' ') %]
+[% hash.list('values').sort.join(' ') %]
+[% SET list = hash.list('each');
+   FOR kindex IN [0,2,4];
+     SET vindex = kindex + 1;
+     SET key = list.${kindex};
+     SET value = list.${vindex};
+     (key AND hash.${key} == value) ? 1 : 0;
+   END %]
+[% SET list = hash.list();
+   FOR entry IN list;
+     SET key = entry.key;
+     SET value = entry.value;
+     (hash.${key} == value) ? 1 : 0;
+   END %]
+--- output
+a b c
+1 2 3
+111
+111
+
+=== Test nsort method
+--- jemplate
+hash_nsort.html
+[% SET a = { '499'='c' '5'='a' '50'='b' } -%]
+[% a.nsort.join(' ') %]
+--- output
+5 50 499
+
+=== Test size method
+--- jemplate
+hash_size.html
+[% SET a = { a=1 b=2 c=3 } -%]
+[% a.size %]
+--- output
+3
+
+=== Test sort method
+--- jemplate
+hash_sort.html
+[% SET a = { ac=1 b=2 aa=3 } -%]
+[% a.sort.join(' ') %]
+--- output
+aa ac b
+
+=== Test values method
+--- jemplate
+hash_values.html
+[% SET a = { a=1 b=2 c=3 } -%]
+[% a.values.nsort.join(' ') %]
+--- output
+1 2 3
+
+*/
@@ -0,0 +1,44 @@
+var t = new Test.Jemplate();
+
+t.plan(3);
+
+t.compile();
+
+var j = new Jemplate();
+
+var test1 = t.state.blocks.shift();
+t.is(
+    j.process('hello', {who: 'Wally'}),
+    test1.data.output,
+    test1.name
+);
+
+var test2 = t.state.blocks.shift();
+t.is(
+    j.process('hello'),
+    test2.data.output,
+    test2.name
+);
+
+var j2 = new Jemplate({DEBUG_UNDEF: true});
+try {
+    j2.process('hello', {whoxxx: 'Bogey'});
+    t.fail('undefined value did not throw error');
+}
+catch(e) {
+    t.is(
+        String(e),
+        "undefined value found while using DEBUG_UNDEF",
+        "undefined value throws error"
+    );
+}
+
+/* Test
+=== Test second jemplate file (jemplate2.js)
+--- output
+Hello, Wally!
+=== Test undefined value
+--- output
+Hello, !
+
+*/
@@ -0,0 +1,19 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(1);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test join
+--- jemplate
+join.html
+[% a = ['foo', 'bar', 'baz'] %]
+[%- a.join('::') -%]
+--- output
+foo::bar::baz
+*/
@@ -0,0 +1,53 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(2);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test list methods
+--- jemplate
+list.html
+[% SET a1 = [ 'one', 'two', 'three'] -%]
+[% CALL a1.push('four') -%]
+[%- a1.first() %] - [% a1.last() %]
+[% a1.grep('o').join('/') %]
+[% a1.max() %]+[% a1.size() %]
+[% SET a2 = a1.reverse -%]
+[% a2.join('^') %]
+[% a2.slice(1, 2).join('*') %]
+[% SET a3 = [ 5, 9, 'x', 17, 9, 33, 12, 'x', 5] -%]
+[% a3.unique().join(',') %]
+[% a1.unshift('zero').sort().join('!') %]
+[% CALL a1.shift(); CALL a1.pop(); a1.join('_') %]
+[% CALL a3.splice(2,1); CALL a3.splice(-2,1); a3.nsort.join('~') %]
+[% SET a4 = [11, 22, 33] -%]
+[% SET a5 = [44, 55, 66] -%]
+[% SET a6 = [77, 88, 99] -%]
+[% SET a7 = a4.merge(a5, 'foo', a6) -%]
+[% a7.join("'") %]
+--- output
+one - four
+one/two/four
+3+4
+four^three^two^one
+three*two
+5,9,x,17,33,12
+four!one!three!two!zero
+one_three_two
+5~5~9~9~12~17~33
+11'22'33'44'55'66'foo'77'88'99
+=== Test sort of hash
+--- jemplate
+list_of_hash.html
+[% SET ll = [ {'a' => 9}, {'a' =>  1}, {'a' => 3} ] -%]
+[%- FOR hash = ll.sort('a') -%]
+[%- hash.a -%]:
+[%- END -%]
+--- output
+1:3:9:
+*/
@@ -0,0 +1,49 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate'
+};
+
+t.plan(2);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== INCLUDE should localize
+--- context
+{"thing":"foo"}
+--- jemplate
+localise1.html
+[% thing %]
+[% SET thing = 'bar' -%]
+[% thing %]
+[% INCLUDE myblock -%]
+[% thing %]
+[% BLOCK myblock -%]
+[% SET thing = 'baz' -%]
+[% END -%]
+--- output
+foo
+bar
+bar
+
+=== PROCESS should not localize
+--- context
+{"thing":"foo"}
+--- jemplate
+localise2.html
+[% thing %]
+[% SET thing = 'bar' -%]
+[% thing %]
+[% PROCESS myblock -%]
+[% thing %]
+[% BLOCK myblock -%]
+[% SET thing = 'baz' -%]
+[% END -%]
+--- output
+foo
+bar
+baz
+
+*/
@@ -0,0 +1,83 @@
+
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(5);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test loop.index
+--- jemplate
+loop1.html
+[%- FOR i IN [0,1,2,3] -%]
+[%- i -%]:[%- loop.index %]
+[% END -%]
+--- output
+0:0
+1:1
+2:2
+3:3
+
+=== Test loop.first && loop.last
+--- jemplate
+loop2.html
+[%- FOR i IN [0,1,2,3] -%]
+    [%- IF loop.first -%]
+[start]
+    [%- END -%]
+    [%- i -%]
+    [%- IF loop.last -%]
+[end]
+    [%- END -%]
+[%- END %]
+--- output
+[start]0123[end]
+
+=== Test loop.count
+--- jemplate
+loop3.html
+[%- FOR i IN [0,1,2,3] -%]
+    [%- i -%]:[%-loop.count%]
+[% END -%]
+--- output
+0:1
+1:2
+2:3
+3:4
+
+=== Test loop.size && loop.max
+--- jemplate
+loop4.html
+[%- FOR i IN [0,1,2,3] -%]
+    [%- i -%]:[%-loop.size%]:[%-loop.max%]
+[% END -%]
+--- output
+0:4:3
+1:4:3
+2:4:3
+3:4:3
+
+=== Test loop.prev && loop.next
+--- jemplate
+loop5.html
+[%- FOR i IN [0,1,2,3] -%]
+    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
+[% END -%]
+[%- FOR i IN {a => 0, b => 1, c => 3, d => 4 } -%]
+    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
+[% END -%]
+--- output
+0::1
+1:0:2
+2:1:3
+3:2:
+a::b
+b:a:c
+c:b:d
+d:c:
+
+*/
@@ -0,0 +1,69 @@
+function TheName(name) {
+    this.name = name;
+}
+
+TheName.prototype.getName = function() {
+   return this.name;
+}
+
+TheName.prototype.setName = function(name) {
+   this.name = name;
+}
+
+var theName = new TheName('larry');
+
+// Begin test
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate'
+};
+
+t.plan(4);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Basic hash
+--- jemplate
+myhashname.html
+[%- JAVASCRIPT -%]
+stash.set( "object", { name: 'Wally' } );
+[%- END -%]
+Hello, [% object.name %]
+--- output
+Hello, Wally
+
+=== Object attribute
+--- jemplate
+myobjectnameattr.html
+[%- JAVASCRIPT -%]
+stash.set( "object", new TheName('Larry') );
+[%- END -%]
+Hello, [% object.name %]
+--- output
+Hello, Larry
+
+=== Object accessor
+--- jemplate
+myobjectnameac.html
+[%- JAVASCRIPT -%]
+var obj = new TheName('Bally');
+stash.set( "objectName", obj.getName() );
+[%- END -%]
+Hello, [% objectName %]
+--- output
+Hello, Bally
+
+=== Basic accessor2
+--- jemplate
+myobjectnameac2.html
+[%- JAVASCRIPT -%]
+stash.set( "object", new TheName('Mally') );
+[%- END -%]
+Hello, [% object.getName() %]
+--- output
+Hello, Mally
+
+*/
@@ -0,0 +1,34 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(1);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test plugins
+--- jemplate
+plugins_basic.html
+[% USE dummy('simple param') -%]
+[% dummy.simple %]
+#
+[% dummy.params('one', 'two') %]
+#
+[% dummy.what %]
+#
+[% dummy.get_what %]
+#
+--- output
+This text came from the plugin
+#
+params: one, two
+#
+simple param
+#
+what: simple param
+#
+
+*/
@@ -0,0 +1,23 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate'
+};
+
+t.plan(1);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Setting a value into a hash works
+--- jemplate
+test.html
+[% testhash = {} -%]
+[% testkey = 'bob' -%]
+[% testhash.$testkey = 'bozo' -%]
+Value: [% testhash.$testkey %]<br />
+--- output
+Value: bozo<br />
+
+*/
@@ -0,0 +1,208 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process',
+    context: 'evaluate',
+    raw_context: 'raw_context'
+};
+
+t.plan(7);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Both .keys and .keys() works
+--- context
+{"hash":{"foo":"FOO","bar":"BAR"}}
+--- jemplate
+stash-functions1.html
+[% hash.keys.sort.join('+') %]
+[% hash.keys().sort().join('+') %]
+--- output
+bar+foo
+bar+foo
+
+=== Disambiguatation of .keys and .keys()
+--- context
+{"hash":{"keys":"foo","values":"bar"}}
+--- jemplate
+stash-functions2.html
+[% hash.keys %]
+[% hash.keys().join('+') %]
+--- output
+foo
+keys+values
+
+=== Stashed Function
+--- raw_context
+{ hash: { 
+    noarg: function() { return "noarg" },
+    arg:   function(arg) { return "arg: " + arg }
+} }
+--- jemplate
+stash-functions3.html
+[% hash.noarg %]
+[% hash.noarg() %]
+[% hash.arg("abc") %]
+--- output
+noarg
+noarg
+arg: abc
+
+=== Basic Global Scope Access
+--- raw_context
+{}
+--- jemplate
+global-scope-access.html
+#1
+[% GLOBAL.global_foo %]
+#2
+[% GLOBAL.global_object.str %]
+#3
+[% GLOBAL.global_object.func_sum(1,1) %]
+#4
+[% GLOBAL.global_multiply(1,10) %]
+#5
+[% global_foo %]
+#6
+[% global_object.str %]
+#7
+[% global_object.func_sum(1,1) %]
+#8
+[% global_multiply(1,10) %]
+eof
+--- output
+#1
+global_foo
+#2
+global_object_str
+#3
+2
+#4
+10
+#5
+
+#6
+
+#7
+
+#8
+
+eof
+
+=== Advanced Global Scope Access
+--- raw_context
+{}
+--- jemplate
+global-scope-access2.html
+#1
+[% 
+	global_foo = 'local_foo'; #creates local variable
+	GLOBAL.global_foo;
+%]
+#2
+[% global_foo %]
+#3
+[% 
+	local_var = "foo"; #new variables are always local
+	GLOBAL.local_var; #empty	
+%]
+#4
+[% 
+	GLOBAL.new_global_var = "new_global_var"; #new global vars could be created only this way
+	new_global_var; #empty
+%]
+#5
+[% GLOBAL.new_global_var; %]
+#6
+[%
+	new_global_var = "local_value";
+	new_global_var; #not empty
+%]
+#7
+[% GLOBAL.new_global_var; %]
+#8
+[% 
+	global_foo = "global_foo2";
+	global_foo;
+%]
+#9
+[%
+	LOCAL.global_foo = "masked"; #LOCAL access	
+	global_foo; 
+%]
+--- output
+#1
+global_foo
+#2
+local_foo
+#3
+
+#4
+
+#5
+new_global_var
+#6
+local_value
+#7
+new_global_var
+#8
+global_foo2
+#9
+masked
+
+=== RAW directive
+--- raw_context
+{}
+--- jemplate
+global-scope-access3.html
+#1
+[%
+	RAW global_foo;
+	global_foo;
+%]
+#2
+[%
+	RAW global_object;
+	global_object.str;
+%]
+#3
+[% global_object.func_sum(1,1); %]
+#4
+[%
+	RAW global_multiply;	
+	global_multiply(1,10);
+%]
+#5
+[%
+	global_object.str = 'new_str';
+	GLOBAL.global_object.str;	
+%]
+--- output
+#1
+global_foo
+#2
+global_object_str
+#3
+2
+#4
+10
+#5
+new_str
+
+=== Function property access
+--- raw_context
+{}
+--- jemplate
+global-scope-access4.html
+#1
+[%
+	#RAW global_multiply;	
+	#global_multiply.function_property;
+	'function_property';
+%]
+--- output
+#1
+function_property
+
+*/
@@ -0,0 +1,132 @@
+var t = new Test.Jemplate();
+
+var filters = {
+    jemplate: 'jemplate_process'
+};
+
+t.plan(10);
+t.filters(filters);
+t.run_is('jemplate', 'output');
+
+/* Test
+=== Test chunk method
+--- jemplate
+string_chunk.html
+[% SET a = '1234567890' -%]
+[% a.chunk.join(' ') %]
+[% a.chunk(2).join(' ') %]
+[% a.chunk(3).join(' ') %]
+[% a.chunk(9).join(' ') %]
+[% a.chunk(10).join(' ') %]
+[% a.chunk(-1).join(' ') %]
+[% a.chunk(-2).join(' ') %]
+[% a.chunk(-3).join(' ') %]
+[% a.chunk(-9).join(' ') %]
+[% a.chunk(-10).join(' ') %]
+--- output
+1 2 3 4 5 6 7 8 9 0
+12 34 56 78 90
+123 456 789 0
+123456789 0
+1234567890
+1 2 3 4 5 6 7 8 9 0
+12 34 56 78 90
+1 234 567 890
+1 234567890
+1234567890
+
+=== Test defined method
+--- jemplate
+string_defined.html
+[% SET a = '1' -%]
+[% a.defined ? '1' : '0' %]
+--- output
+1
+
+=== Test hash method
+--- jemplate
+string_hash.html
+[% SET a = 'Hi' -%]
+[% a.hash.value %]
+--- output
+Hi
+
+=== Test list method
+--- jemplate
+string_list.html
+[% SET a = 'Hi' -%]
+[% a.list.0 %]
+--- output
+Hi
+
+=== Test match method
+--- jemplate
+string_match.html
+[% SET a = 'aaa12aaa34aaa56' -%]
+[% a.match('\\\d\\\d').join(' ') %]
+--- output
+12 34 56
+
+=== Test repeat method
+--- jemplate
+string_repeat.html
+[% SET a = 'aaa' -%]
+[% a.repeat(3) %]
+[% a.repeat() %]
+--- output
+aaaaaaaaa
+aaa
+
+=== Test replace method
+--- jemplate
+string_replace.html
+[% SET a = 'aaa12aaa34aaa56' -%]
+[% a.replace('\\\d\\\d', 'bb') %]
+[% a.replace('\\\d\\\d') %]
+--- output
+aaabbaaabbaaabb
+aaaaaaaaa
+
+=== Test search method
+--- jemplate
+string_search.html
+[% SET a = 'aaa12aaa34aaa56' -%]
+[% a.search('\\\d\\\d') ? 1 : 0 %]
+[% a.search('w') ? 1 : 0 %]
+--- output
+1
+0
+
+=== Test size method
+--- jemplate
+string_size.html
+[% SET a = '1' -%]
+[% a.defined ? '1' : '0' %]
+--- output
+1
+
+=== Test split method
+--- jemplate
+string_split.html
+[% SET a = 'aaa12aaa34aaa' -%]
+[% a.split('\\\d\\\d').join(' ') %]
+[% SET a = '1aaa2aaa3aaa4' -%]
+[% a.split('aaa').join(' ') %]
+--- output
+aaa aaa aaa
+1 2 3 4
+
+*/
+
+/*
+=== Test length method
+--- jemplate
+string_length.html
+[% SET a = 'Hi' -%]
+[% a.length %]
+[% SET a = 10 -%]
+[% a.length %]
+--- output
+2
+
+*/
@@ -0,0 +1,16 @@
+<html>
+<head>
+    <script type="text/javascript" src="js/jquery.js"></script>
+
+    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
+    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
+    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
+    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
+
+    <script type="text/javascript" src="runtime.js"></script>
+    <script type="text/javascript" src="jt-greeting.js"></script>
+</head>
+<body onload="Jemplate.process('importantMessage', 'ajax', document.body)">
+It didn't work!
+</body>
+</html>
@@ -0,0 +1,16 @@
+<html>
+<head>
+    <title>[% index_title %]</title>
+    <script type="text/javascript" src="lib/Test/Harness.js"></script>
+    <script type="text/javascript" src="lib/Test/Harness/Browser.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+    var t = new Test.Harness.Browser();
+    
+    t.runTests(
+        "[% all_test_files.join("\",\n        \"") %]"
+    );
+</script>
+</body>
+</html>
@@ -0,0 +1,16 @@
+<html>
+<head>
+    <title>[% index_title | html %] - [% test_file | html %]</title>
+    <script type="text/javascript">
+        var testBaseCurrentScript = '[% test_file %]';
+    </script>
+    <script type="text/javascript" src="lib/Test/Builder.js"></script>
+    <script type="text/javascript" src="lib/Test/Base.js"></script>
+[% FOR lib = import_libs -%]
+    <script type="text/javascript" src="[% lib %]"></script>
+[% END -%]
+</head>
+<body>
+    <script type="text/javascript" src="[% test_file %]"></script>
+</body>
+</html>
@@ -0,0 +1,88 @@
+.PHONY: all prepare-var-jt var clean help
+.PHONY: test-all test test-jquery test-yui runtime runtime-jquery runtime-yui
+
+T_JS := $(wildcard ../t/*t.js)
+T_HTML := $(T_JS:../t/%.t.js=var/%.t.html)
+JEMPLATE := perl -I../../lib ../../jemplate
+RUNTIME_JS := var/runtime.js
+
+help:
+	@echo
+	@echo -e "\t$(MAKE) test-all"
+	@echo
+	@echo -e "\t$(MAKE) test"
+	@echo -e "\t$(MAKE) test-jquery"
+	@echo -e "\t$(MAKE) test-yui"
+	@echo
+	@echo -e "\t$(MAKE) run"
+	@echo -e "\t$(MAKE) clean"
+	@echo
+
+all: \
+	var \
+	var/test.html \
+	var/lib \
+	var/js \
+	var/t \
+	$(T_HTML) \
+	var/index.html \
+	var/runtime.js \
+	var/jt.js \
+	var/jt-greeting.js
+
+var/lib:
+	(cd var && ln -s ../../lib lib)
+
+var/js:
+	(cd var && ln -s ../../js js)
+
+var/t:
+	(cd var && ln -s ../../t t)
+
+test-all:
+	(cd ../bin && ../test-run)
+
+test: all runtime run
+
+test-jquery: all runtime-jquery run
+
+test-yui: all runtime-yui run
+
+run:
+	(cd var && ../../bin/daemon -p 8081)
+
+runtime:
+	$(JEMPLATE) --runtime=standard > $(RUNTIME_JS)
+
+runtime-jquery:
+	$(JEMPLATE) --runtime=jquery > $(RUNTIME_JS)
+
+runtime-yui:
+	$(JEMPLATE) --runtime=yui > $(RUNTIME_JS)
+
+var:
+	mkdir -p var
+
+var/test.html:
+	(cd var && ln -s ../../t/test.html)
+
+$(T_HTML): var/%.t.html: ../template/test.html ../t/%.t.js
+	../bin/render-template test.html $@
+
+var/index.html: ../template/index.html ../t/*.t.js
+	../bin/render-template index.html $@
+
+$(RUNTIME_JS):
+	$(JEMPLATE) --runtime > $(RUNTIME_JS)
+
+var/jt.js: prepare-var-jt
+	$(JEMPLATE) --compile var/jt/* > $@
+
+var/jt-greeting.js:
+	$(JEMPLATE) --compile ../jemplate2 --start-tag='<!' --end-tag='!>' --any-case --post-chomp > $@
+
+prepare-var-jt:
+	../bin/prepare-var-jt
+
+clean:
+	rm -rf var
@@ -0,0 +1,9 @@
+index_title: Jemplate Testing
+import_libs:
+  - js/Test/Jemplate.js
+  - runtime.js
+  - jt.js
+  - jt-greeting.js
+  - js/jemplate_dummy_plugin.js
+  - js/global.js
+templates: ../template
@@ -1,66 +0,0 @@
-.PHONY: all prepare-var-jt var clean help
-.PHONY: test-all test test-jquery test-yui runtime runtime-jquery runtime-yui
-
-T_JS := $(wildcard t/*t.js)
-T_HTML := $(T_JS:t/%.t.js=var/%.t.html)
-JEMPLATE := perl -I../lib ../jemplate
-RUNTIME_JS := var/runtime.js
-
-help:
-	@echo
-	@echo -e "\t$(MAKE) test-all"
-	@echo
-	@echo -e "\t$(MAKE) test"
-	@echo -e "\t$(MAKE) test-jquery"
-	@echo -e "\t$(MAKE) test-yui"
-	@echo -e "\t$(MAKE) clean"
-	@echo
-
-all: var $(T_HTML) var/index.html var/runtime.js var/jt.js var/jt-greeting.js
-	(cd var && ln -snf ../js . && ln -snf ../t/*.js .)
-
-test-all:
-	./bin/test-run
-
-test: all runtime
-	(cd var && ../bin/daemon -p 8081)
-
-test-jquery: all runtime-jquery
-	(cd var && ../bin/daemon -p 8081)
-
-test-yui: all runtime-yui
-	(cd var && ../bin/daemon -p 8081)
-
-runtime:
-	$(JEMPLATE) --runtime > $(RUNTIME_JS)
-
-runtime-jquery:
-	$(JEMPLATE) --runtime=jquery > $(RUNTIME_JS)
-
-runtime-yui:
-	$(JEMPLATE) --runtime=yui > $(RUNTIME_JS)
-
-var:
-	mkdir -p var
-	rsync -Cav htdocs/ var/
-
-$(T_HTML): var/%.t.html: tt/test.html t/%.t.js
-	./bin/render-template $< $@
-
-var/index.html: tt/index.html t/*.t.js
-	./bin/render-template $< $@
-
-$(RUNTIME_JS):
-	$(JEMPLATE) --runtime > $(RUNTIME_JS)
-
-var/jt.js: prepare-var-jt
-	$(JEMPLATE) --compile var/jt/* > $@
-
-var/jt-greeting.js:
-	$(JEMPLATE) --compile jt-greeting --start-tag='<!' --end-tag='!>' --any-case --post-chomp > $@
-
-prepare-var-jt:
-	./bin/prepare-var-jt
-
-clean:
-	rm -rf var
@@ -1,125 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-use Getopt::Long    qw[];
-use HTTP::Daemon    qw[];
-use HTTP::Response  qw[];
-use HTTP::Status    qw[RC_FORBIDDEN];
-use LWP::MediaTypes qw[guess_media_type];
-use Path::Class     qw[];
-use Pod::Usage      qw[];
-use JSON;
-
-$SIG{'PIPE'} = 'IGNORE';
-
-Getopt::Long::Configure( 'no_ignore_case' );
-Getopt::Long::Configure( 'bundling' );
-Getopt::Long::GetOptions(
-    'p|port=i' => \( my $port = 8080 ),
-    'H|host=s' => \( my $host ),
-    'h|help|?' => sub { Pod::Usage::pod2usage( { -verbose => 1, -exitval => 0 } ) },
-    'm|man'    => sub { Pod::Usage::pod2usage( { -verbose => 2, -exitval => 0 } ) },
-) or exit 1;
-
-#my $base = Path::Class::File->new($0)->parent->parent->absolute;
-my $base = Path::Class::Dir->new; # Use the CWD
-my %args = ( LocalPort => $port, ReuseAddr => 1 );
-
-if ( defined $host ) {
-    $args{LocalHost} = $host;
-}
-
-my $daemon = HTTP::Daemon->new(%args)
-  or die("Failed to create daemon: $!\n");
-
-print "Please contact me at: <URL:", $daemon->url, ">\n";
-
-while ( my $connection = $daemon->accept ) {
-
-    while ( my $request = $connection->get_request ) {
-
-        if ( $request->url->path =~ /^\/ajax\b/ ) {
-            my $response = HTTP::Response->new( 200, 'OK' );
-            $response->header( 'Connection'    => 'close' );
-            $response->header( 'Cache-Control' => 'no-cache, max-age=0' );
-            my $content = encode_json { message => 'abcdefghijklmnopqrstuvwxy' };
-            $response->content( $content );
-            $response->content_length( length $content );
-            $response->content_type( "text/plain" );
-            $response->last_modified( time );
-
-            $connection->send_response( $response );
-
-            next;
-        }
-
-        my $file = $request->url->path =~ m|^/share|
-          ? $base->parent->file( $request->url->path )
-          : $request->url->path eq '/'
-              ? $base->file('index.html')
-              : $base->file( $request->url->path );
-
-        $file = $file->cleanup->absolute;
-
-        if ( -e $file && $request->method eq 'GET' ) {
-
-            print "$file\n";
-
-            my $stat     = $file->stat;
-            my $type     = ( guess_media_type( $file->stringify ) )[0];
-            my $response = HTTP::Response->new( 200, 'OK' );
-            $response->header( 'Connection'    => 'close' );
-            $response->header( 'Cache-Control' => 'no-cache, max-age=0' );
-
-            if ( 0 && $request->if_modified_since || 0 == $stat->mtime ) {
-                $response->code(304);
-                $response->message('Not Modified');
-            }
-            else {
-                $response->content( scalar $file->slurp );
-                $response->content_length( $stat->size );
-                $response->content_type($type);
-                $response->last_modified( $stat->mtime );
-            }
-
-            $connection->send_response($response);
-        }
-        else {
-
-            print "$file FORBIDDEN\n";
-
-            $connection->send_error(RC_FORBIDDEN);
-            $connection->close;
-        }
-    }
-}
-
-__END__
-
-=head1 NAME
-
-daemon - Jemplate test daemon
-
-=head1 SYNOPSIS
-
-daemon [-p]
-
- Options:
-   -H --host   Host to listen to
-   -p --port   Port to listen to
-   -m --man    Displays manpage
-   -h --help   Displays this help
-
-=head1 COMMAND LINE OPTIONS
-
-=head2 --host
-
-Host to listen to. Defaults to listen to the given port on all interfaces.
-
-=head2 --port
-
-Port to listen to. Defaults to listen to 8080.
-
-=cut
@@ -1,25 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use IO::All;
-
-my $output_dir = 'var/jt';
-
-my %jemplates;
-
-for my $test (glob 't/*.t.js') {
-    my $file_contents = io($test)->all;
-    my (@jemplates) = ($file_contents =~ /(^--- jemplate\n.*?)(?=^---|\z)/gsm);
-    for my $jemplate (@jemplates) {
-        my ($ignore, $jemplate_file, $jemplate_content) =
-            split /\n/, $jemplate, 3;
-        $jemplates{$jemplate_file} = $jemplate_content;
-    }
-}
-
-mkdir $output_dir unless -d $output_dir;
-for my $jemplate_file (sort keys %jemplates) {
-    io("$output_dir/$jemplate_file")->print($jemplates{$jemplate_file});
-}
-
@@ -1,41 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use Template;
-use IO::All;
-#use YAML;
-
-my ($template_path, $output_path) = @ARGV;
-
-my $test_file_name = $output_path;
-#$test_file_name =~ s/^/..\// or die;
-$test_file_name =~ s/\.html$/.js/ or die;
-$test_file_name =~ s/^var\///;
-
-my $t = Template->new(
-    {
-        INCLUDE_PATH => ['.', 'tt/'],
-    }
-);
-
-my %config = (
-    import_libs => [qw{
-      js/Test/Jemplate.js
-      runtime.js
-      jt.js
-      jt-greeting.js
-    }]
-);
-#my $config = YAML::LoadFile('../config.yaml');
-my $data = {
-    %config,
-    test_file => $test_file_name,
-    all_test_files => [ map { s/^var\///; $_ } glob('var/*.t.html') ],
-#    all_test_files => [ grep { !m/plugins/ } glob('*.t.html') ]
-};
-my $result;
-
-$t->process($template_path, $data, \$result) or die $t->error;
-
-io($output_path)->print($result);
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-JEMPLATE="perl -I../.. ../../jemplate"
-DAEMON_PID=
-
-function run() {
-    $JEMPLATE $* > runtime.js
-    echo -e "\n"
-    echo -e "*** Testing: $*"
-    echo -e "*** Press return when you're ready to continue ***";
-    read;
-}
-
-function quit() {
-    [ -n "$DAEMON_PID" ] && kill -9 $DAEMON_PID
-    exit
-}
-
-trap "quit" SIGINT SIGTERM
-make all
-cd var
-../bin/daemon -p 8081 &
-DAEMON_PID=$!
-
-run --runtime=standard
-run --runtime=yui
-run --runtime=jquery
-
-quit
@@ -1,90 +0,0 @@
-/*==============================================================================
-Subclass - Define a Class potentially as a Subclass in JavaScript
-
-DESCRIPTION:
-
-
-
-AUTHORS:
-
-    Ingy döt Net <ingy@cpan.org>
-
-COPYRIGHT:
-
-Copyright Ingy döt Net 2006. All rights reserved.
-
-Subclass.js is free software. 
-
-This library is free software; you can redistribute it and/or modify it
-under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or (at
-your option) any later version.
-
-This library is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-General Public License for more details.
-
-    http://www.gnu.org/copyleft/lesser.txt
-
- =============================================================================*/
-
-/*==============================================================================
-Subclass - this can be used to create new classes
- =============================================================================*/
-
-Subclass = function(name, base) {
-    if (!name) die("Can't create a subclass without a name");
-
-    var parts = name.split('.');
-    var subclass = window;
-    for (var i = 0; i < parts.length; i++) {
-        if (! subclass[parts[i]])
-            subclass[parts[i]] = function() {
-                try { this.init() } catch(e) {}
-            };
-        subclass = subclass[parts[i]];
-    }
-
-    if (base) {
-        var baseclass = eval('new ' + base + '()');
-        subclass.prototype = baseclass;
-        subclass.prototype.baseclass = base;
-        subclass.prototype.superfunc = Subclass.generate_superfunc();
-    }
-    subclass.prototype.classname = name;
-    return subclass.prototype;
-}
-
-Subclass.generate_superfunc = function() {
-    return function(func) {
-        var p;
-        var found = false;
-        var caller_func = arguments.callee.caller;
-        for (var b = this.classname; b; b = p.baseclass) {
-            p = eval(b + '.prototype');
-            if (! found) {
-                if (p[func] && p[func] == caller_func)
-                    found = true;
-                continue;
-            }
-            if (p[func] && p[func] != caller_func)
-                return p[func];
-        }
-        die(
-            "No superfunc function for: " + func + "\n" +
-            "baseclass was: " + this.baseclass + "\n" +
-            "caller was: " + arguments.callee.caller
-        );
-    }
-}
-
-/*
-
-=head1 NAME
-
-Subclass - Define a class or subclass
-
-=cut
-
-*/
@@ -1,595 +0,0 @@
-proto = Subclass('Test.Base');
-
-Test.Base.VERSION = '0.13';
-
-proto.init = function() {
-    this.builder = Test.Builder.instance();
-    this.builder.reset();
-    this.block_class = 'Test.Base.Block';
-    this.state = {};
-    this.state.compiled = false;
-    this.state.spec_url = testBaseCurrentScript;
-    this.state.spec_content = null;
-    this.state.filters_map = {};
-    this.state.blocks = [];
-}
-
-proto.spec = function(url) {
-    this.state.spec_url = url;
-}
-
-proto.filters = function(obj) {
-    this.state.filters_map = obj;
-}
-
-proto.run_is = function(x, y) {
-    try {
-        this.compile();
-        var blocks =  this.state.blocks;
-        for (var i = 0; i < blocks.length; i++) {
-            var block = blocks[i];
-            if (! this.verify_block(block, x, y)) continue;
-            this.is(block.data[x], block.data[y], block.name);
-        }
-    }
-    catch(e) {
-        // alert(e);
-        throw(e);
-    }
-}
-
-proto.plan = function(number) {
-    var cmds = {tests: number};
-    return this.builder.plan(cmds);
-}
-
-proto.pass = function(name) {
-    return this.builder.ok(true, name);
-}
-
-proto.fail = function(name) {
-    return this.builder.ok(false, name);
-}
-
-proto.is = function (got, expect, desc) {
-    return this.builder.isEq(got, expect, desc);
-};
-
-proto.isnt = function (got, expect, desc) {
-    return this.builder.isntEq(got, expect, desc);
-};
-
-proto.like = function (val, regex, desc) {
-    return this.builder.like(val, regex, desc);
-};
-
-proto.unlike = function (val, regex, desc) {
-    return this.builder.unlike(val, regex, desc);
-};
-
-proto.compile = function() {
-    if (this.state.compiled) return;
-    this.get_spec();
-    this.create_blocks();
-    this.state.compiled = true;
-}
-
-proto.get_spec = function() {
-    var url = this.state.spec_url;
-    if (url == undefined)
-        throw('no spec provided');
-
-    var text = Ajax.get(url);
-    text = text.replace(/(?:.|\n)*\/\*\s*test.*\n/i, '');
-    text = text.replace(/\n\*\/(?:.|\n)*/, '');
-    this.state.spec_content = text;
-}
-
-proto.create_blocks = function() {
-    var text = this.state.spec_content;
-    // This is what we want but Safari is broken with ^ and m flag
-    // var hunks = text.split(/(?=(\A|^)===)/m);
-    // This works for now but is too fragile.
-    var hunks = text.split(/(?====)/);
-    for (var i = 0; i < hunks.length; i++) {
-        var hunk = hunks[i];
-        if (! hunk.match(/^===/)) continue;
-        var block = this.make_block(hunk);
-        this.state.blocks.push(block);
-    }
-}
-
-proto.make_block = function(hunk) {
-    var block = eval('new ' + this.block_class + '()');
-    if (! hunk.match(/^===/)) throw("Invalid Hunk");
-
-    var index = hunk.indexOf('\n') + 1;
-    if (! index) throw('Invalid Hunk.');
-    var name = hunk.substr(4, index - 5);
-    hunk = hunk.substr(index); 
-    block.name = name.replace(/^\s*(.*?)\s*$/, '$1');
-
-    var chunks = [];
-    while (hunk.indexOf('\n---') >= 0) {
-        index = hunk.indexOf('\n---') + 1;
-        var chunk = hunk.substr(0, index);
-        hunk = hunk.substr(index);
-        chunks.push(chunk);
-    }
-    chunks.push(hunk);
-
-    for (var i = 0; i < chunks.length; i++) {
-        var chunk = chunks[i];
-        index = chunk.indexOf('\n');
-        if (index < 0) throw('xxx1');
-        var line1 = chunk.substr(0, index);
-        var section_data = chunk.substr(index + 1);
-        line1 = line1.replace(/^---\s*/, '');
-        if (! line1.length) throw('xxx2');
-        var section_name = '';
-        var section_filters = [];
-        if (line1.indexOf(':') >= 0) {
-            index = line1.indexOf(':');
-            section_data = line1.substr(index + 1).
-                replace(/^\s*(.*?)\s*$/, '$1');
-            line1 = line1.substr(0, index);
-        }
-        if (! line1.match(/^\w+$/)) throw('xxx3');
-        section_name = line1;
-        block.add_section(section_name, section_filters, section_data);
-    }
-    return block;
-}
-
-proto.verify_block = function(block) {
-    block.apply_filters(this.state.filters_map);
-    for (var i = 1; i < arguments.length; i++) {
-        var value = arguments[i];
-        if (typeof block.data[value] == 'undefined') return false;
-    }
-    return true;
-}
-
-//------------------------------------------------------------------------------
-proto = Subclass('Test.Base.Block');
-
-proto.init = function() {
-    this.name = null;
-    this.description = null;
-    this.sections = [];
-    this.data = {};
-    this.filters = {};
-    this.filter_object = new Test.Base.Filter();
-}
-
-proto.add_section = function(name, filters, data) {
-    this.sections.push(name);
-    this.data[name] = data;
-    this.filters[name] = filters;
-}
-
-proto.apply_filters = function(filter_overrides) {
-    var sections = this.sections;
-    for (var i = 0; i < sections.length; i++) {
-        var section = sections[i];
-        var filters = ['normalize', 'trim'];
-        this.push_filters(filters, this.filters[section]);
-        this.push_filters(filters, filter_overrides[section]);
-        this.filter_section(section, filters);
-    }
-}
-
-proto.push_filters = function(a1, a2) {
-    if (typeof a2 == 'undefined')
-        return;
-    if (typeof a2 == 'string')
-        a1.push(a2);
-    else {
-        for (var i = 0; i < a2.length; i++) {
-            a1.push(a2[i]);
-        }
-    }
-}
-
-proto.filter_section = function(section, filters) {
-    var data = this.data[section];
-    for (var i = 0; i < filters.length; i++) {
-        var filter = filters[i];
-        if (typeof window[filter] == 'function')
-            data = (window[filter]).call(this, data, this);
-        else if (typeof this.filter_object[filter] == 'function')
-            data = (this.filter_object[filter]).call(this, data, this);
-        else
-            throw('No function for filter: ' + filter);
-    }
-    this.data[section] = data;
-}
-
-//------------------------------------------------------------------------------
-proto = Subclass('Test.Base.Filter');
-
-proto.ajax_get = function(url) {
-    url = url.replace(/n+$/, '');
-    return Ajax.get(url);
-}
-
-proto.trim = function(content, block) {
-    var result = content.replace(/^\s*\n/, '');
-    result = result.replace(/\n\s*$/, '\n');
-    return result;
-}
-
-proto.normalize = function(content, block) {
-    return content;
-}
-
-proto.evaluate = function(content, block) {
-    var javascript = content;
-    var object = JSON.parse(javascript);
-    return object;
-}
-
-//------------------------------------------------------------------------------
-// Debugging Support
-//------------------------------------------------------------------------------
-
-function XXX(msg) {
-    //if (! confirm(arguments.join('\n')))
-    if (! confirm(msg))
-        throw("terminated...");
-}
-
-function JJJ(obj) {
-    XXX(JSON.stringify(obj));
-}
-
-//------------------------------------------------------------------------------
-// Ajax support
-//------------------------------------------------------------------------------
-if (! this.Ajax) Ajax = {};
-
-Ajax.get = function(url, callback) {
-    var req = new XMLHttpRequest();
-    req.open('GET', url, Boolean(callback));
-    return Ajax._send(req, null, callback, url);
-}
-
-Ajax.post = function(url, data, callback) {
-    var req = new XMLHttpRequest();
-    req.open('POST', url, Boolean(callback));
-    req.setRequestHeader(
-        'Content-Type', 
-        'application/x-www-form-urlencoded'
-    );
-    return Ajax._send(req, data, callback, url);
-}
-
-Ajax._send = function(req, data, callback, url) {
-    if (callback) {
-        req.onreadystatechange = function() {
-            if (req.readyState == 4) {
-                if(req.status == 200)
-                    callback(req.responseText);
-            }
-        };
-    }
-    req.send(data);
-    if (!callback) {
-        if (req.status != 200)
-            throw('Request for "' + url +
-                  '" failed with status: ' + req.status);
-        return req.responseText;
-    }
-}
-
-//------------------------------------------------------------------------------
-// Cross-Browser XMLHttpRequest v1.1
-//------------------------------------------------------------------------------
-/*
-Emulate Gecko 'XMLHttpRequest()' functionality in IE and Opera. Opera requires
-the Sun Java Runtime Environment <http://www.java.com/>.
-
-by Andrew Gregory
-http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/
-
-This work is licensed under the Creative Commons Attribution License. To view a
-copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or send
-a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305,
-USA.
-*/
-
-// IE support
-if (window.ActiveXObject && !window.XMLHttpRequest) {
-  window.XMLHttpRequest = function() {
-    return new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') != -1) ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
-  };
-}
-
-// Opera support
-if (window.opera && !window.XMLHttpRequest) {
-  window.XMLHttpRequest = function() {
-    this.readyState = 0; // 0=uninitialized,1=loading,2=loaded,3=interactive,4=complete
-    this.status = 0; // HTTP status codes
-    this.statusText = '';
-    this._headers = [];
-    this._aborted = false;
-    this._async = true;
-    this.abort = function() {
-      this._aborted = true;
-    };
-    this.getAllResponseHeaders = function() {
-      return this.getAllResponseHeader('*');
-    };
-    this.getAllResponseHeader = function(header) {
-      var ret = '';
-      for (var i = 0; i < this._headers.length; i++) {
-        if (header == '*' || this._headers[i].h == header) {
-          ret += this._headers[i].h + ': ' + this._headers[i].v + '\n';
-        }
-      }
-      return ret;
-    };
-    this.setRequestHeader = function(header, value) {
-      this._headers[this._headers.length] = {h:header, v:value};
-    };
-    this.open = function(method, url, async, user, password) {
-      this.method = method;
-      this.url = url;
-      this._async = true;
-      this._aborted = false;
-      if (arguments.length >= 3) {
-        this._async = async;
-      }
-      if (arguments.length > 3) {
-        // user/password support requires a custom Authenticator class
-        opera.postError('XMLHttpRequest.open() - user/password not supported');
-      }
-      this._headers = [];
-      this.readyState = 1;
-      if (this.onreadystatechange) {
-        this.onreadystatechange();
-      }
-    };
-    this.send = function(data) {
-      if (!navigator.javaEnabled()) {
-        alert("XMLHttpRequest.send() - Java must be installed and enabled.");
-        return;
-      }
-      if (this._async) {
-        setTimeout(this._sendasync, 0, this, data);
-        // this is not really asynchronous and won't execute until the current
-        // execution context ends
-      } else {
-        this._sendsync(data);
-      }
-    }
-    this._sendasync = function(req, data) {
-      if (!req._aborted) {
-        req._sendsync(data);
-      }
-    };
-    this._sendsync = function(data) {
-      this.readyState = 2;
-      if (this.onreadystatechange) {
-        this.onreadystatechange();
-      }
-      // open connection
-      var url = new java.net.URL(new java.net.URL(window.location.href), this.url);
-      var conn = url.openConnection();
-      for (var i = 0; i < this._headers.length; i++) {
-        conn.setRequestProperty(this._headers[i].h, this._headers[i].v);
-      }
-      this._headers = [];
-      if (this.method == 'POST') {
-        // POST data
-        conn.setDoOutput(true);
-        var wr = new java.io.OutputStreamWriter(conn.getOutputStream());
-        wr.write(data);
-        wr.flush();
-        wr.close();
-      }
-      // read response headers
-      // NOTE: the getHeaderField() methods always return nulls for me :(
-      var gotContentEncoding = false;
-      var gotContentLength = false;
-      var gotContentType = false;
-      var gotDate = false;
-      var gotExpiration = false;
-      var gotLastModified = false;
-      for (var i = 0; ; i++) {
-        var hdrName = conn.getHeaderFieldKey(i);
-        var hdrValue = conn.getHeaderField(i);
-        if (hdrName == null && hdrValue == null) {
-          break;
-        }
-        if (hdrName != null) {
-          this._headers[this._headers.length] = {h:hdrName, v:hdrValue};
-          switch (hdrName.toLowerCase()) {
-            case 'content-encoding': gotContentEncoding = true; break;
-            case 'content-length'  : gotContentLength   = true; break;
-            case 'content-type'    : gotContentType     = true; break;
-            case 'date'            : gotDate            = true; break;
-            case 'expires'         : gotExpiration      = true; break;
-            case 'last-modified'   : gotLastModified    = true; break;
-          }
-        }
-      }
-      // try to fill in any missing header information
-      var val;
-      val = conn.getContentEncoding();
-      if (val != null && !gotContentEncoding) this._headers[this._headers.length] = {h:'Content-encoding', v:val};
-      val = conn.getContentLength();
-      if (val != -1 && !gotContentLength) this._headers[this._headers.length] = {h:'Content-length', v:val};
-      val = conn.getContentType();
-      if (val != null && !gotContentType) this._headers[this._headers.length] = {h:'Content-type', v:val};
-      val = conn.getDate();
-      if (val != 0 && !gotDate) this._headers[this._headers.length] = {h:'Date', v:(new Date(val)).toUTCString()};
-      val = conn.getExpiration();
-      if (val != 0 && !gotExpiration) this._headers[this._headers.length] = {h:'Expires', v:(new Date(val)).toUTCString()};
-      val = conn.getLastModified();
-      if (val != 0 && !gotLastModified) this._headers[this._headers.length] = {h:'Last-modified', v:(new Date(val)).toUTCString()};
-      // read response data
-      var reqdata = '';
-      var stream = conn.getInputStream();
-      if (stream) {
-        var reader = new java.io.BufferedReader(new java.io.InputStreamReader(stream));
-        var line;
-        while ((line = reader.readLine()) != null) {
-          if (this.readyState == 2) {
-            this.readyState = 3;
-            if (this.onreadystatechange) {
-              this.onreadystatechange();
-            }
-          }
-          reqdata += line + '\n';
-        }
-        reader.close();
-        this.status = 200;
-        this.statusText = 'OK';
-        this.responseText = reqdata;
-        this.readyState = 4;
-        if (this.onreadystatechange) {
-          this.onreadystatechange();
-        }
-        if (this.onload) {
-          this.onload();
-        }
-      } else {
-        // error
-        this.status = 404;
-        this.statusText = 'Not Found';
-        this.responseText = '';
-        this.readyState = 4;
-        if (this.onreadystatechange) {
-          this.onreadystatechange();
-        }
-        if (this.onerror) {
-          this.onerror();
-        }
-      }
-    };
-  };
-}
-// ActiveXObject emulation
-if (!window.ActiveXObject && window.XMLHttpRequest) {
-  window.ActiveXObject = function(type) {
-    switch (type.toLowerCase()) {
-      case 'microsoft.xmlhttp':
-      case 'msxml2.xmlhttp':
-        return new XMLHttpRequest();
-    }
-    return null;
-  };
-}
-
-
-//------------------------------------------------------------------------------
-// JSON Support
-//------------------------------------------------------------------------------
-
-/*
-Copyright (c) 2005 JSON.org
-*/
-var JSON = function () {
-    var m = {
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        },
-        s = {
-            'boolean': function (x) {
-                return String(x);
-            },
-            number: function (x) {
-                return isFinite(x) ? String(x) : 'null';
-            },
-            string: function (x) {
-                if (/["\\\x00-\x1f]/.test(x)) {
-                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
-                        var c = m[b];
-                        if (c) {
-                            return c;
-                        }
-                        c = b.charCodeAt();
-                        return '\\u00' +
-                            Math.floor(c / 16).toString(16) +
-                            (c % 16).toString(16);
-                    });
-                }
-                return '"' + x + '"';
-            },
-            object: function (x) {
-                if (x) {
-                    var a = [], b, f, i, l, v;
-                    if (x instanceof Array) {
-                        a[0] = '[';
-                        l = x.length;
-                        for (i = 0; i < l; i += 1) {
-                            v = x[i];
-                            f = s[typeof v];
-                            if (f) {
-                                v = f(v);
-                                if (typeof v == 'string') {
-                                    if (b) {
-                                        a[a.length] = ',';
-                                    }
-                                    a[a.length] = v;
-                                    b = true;
-                                }
-                            }
-                        }
-                        a[a.length] = ']';
-                    } else if (x instanceof Object) {
-                        a[0] = '{';
-                        for (i in x) {
-                            v = x[i];
-                            f = s[typeof v];
-                            if (f) {
-                                v = f(v);
-                                if (typeof v == 'string') {
-                                    if (b) {
-                                        a[a.length] = ',';
-                                    }
-                                    a.push(s.string(i), ':', v);
-                                    b = true;
-                                }
-                            }
-                        }
-                        a[a.length] = '}';
-                    } else {
-                        return;
-                    }
-                    return a.join('');
-                }
-                return 'null';
-            }
-        };
-    return {
-        copyright: '(c)2005 JSON.org',
-        license: 'http://www.crockford.com/JSON/license.html',
-        stringify: function (v) {
-            var f = s[typeof v];
-            if (f) {
-                v = f(v);
-                if (typeof v == 'string') {
-                    return v;
-                }
-            }
-            return null;
-        },
-        parse: function (text) {
-            try {
-                return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
-                        text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
-                    eval('(' + text + ')');
-            } catch (e) {
-                return false;
-            }
-        }
-    };
-}();
-
@@ -1,841 +0,0 @@
-// # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
-
-// Set up namespace.
-if (typeof self != 'undefined') {
-    // Browser
-    if (typeof Test == 'undefined') Test = {PLATFORM: 'browser'};
-    else Test.PLATFORM = 'browser';
-} else if (typeof _global != 'undefined') {
-    //Director
-    if (typeof _global.Test == "undefined") _global.Test = {PLATFORM: 'director'};
-    else _global.Test.PLATFORM = 'director';
-} else {
-    throw new Error("Test.More does not support your platform");
-}
-
-// Constructor.
-Test.Builder = function () {
-    Test.Builder.Instances.push(this.reset());
-    if (!Test.Builder.Test) Test.Builder.Test = this;
-};
-
-// Static variables.
-Test.Builder.globalScope = typeof JSAN != 'undefined'
-  ? JSAN.globalScope
-  :  typeof window != 'undefined'
-    ? window
-    : typeof _global != 'undefined'
-      ? _global
-      : null;
-
-Test.Builder.VERSION = '0.21';
-Test.Builder.Instances = [];
-Test.Builder.lineEndingRx = /\r?\n|\r/g;
-Test.Builder.StringOps = {
-    eq: '==',
-    ne: '!=',
-    lt: '<',
-    gt: '>',
-    ge: '>=',
-    le: '<='
-};
-
-// Stoopid IE.
-Test.Builder.LF = typeof navigator != "undefined"
-    && navigator.userAgent.toLowerCase().indexOf('msie') + 1
-    && Test.Builder.globalScope.opera == undefined
-  ? "\r"
-  : "\n";
-
-// Static methods.
-Test.Builder.die = function (msg) {
-    throw new Error(msg);
-};
-
-Test.Builder._whoa = function (check, desc) {
-    if (!check) return;
-    Test.Builder.die("WHOA! " + desc + Test.Builder.LF +
-                     + "This should never happen! Please contact the author "
-                     + "immediately!");
-};
-
-Test.Builder.typeOf = function (object) {
-    var c = Object.prototype.toString.apply(object);
-    var name = c.substring(8, c.length - 1);
-    if (name != 'Object') return name;
-    // It may be a non-core class. Try to extract the class name from
-    // the constructor function. This may not work in all implementations.
-    if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
-        return RegExp.$1;
-    }
-    // No idea. :-(
-    return name;
-};
-
-Test.Builder.instance = function () {
-    if (!Test.Builder.Test) return new Test.Builder();
-    return Test.Builder.Test;
-};
-
-Test.Builder.create = function () {
-    var ret = new Test.Builder();
-    ret.diag(
-        "Test.Builder.create() has been deprecated. "
-        + "Use new Test.Builder() instead"
-    );
-    return ret;
-};
-
-// Instance methods.
-
-Test.Builder.prototype.reset = function () {
-    this.TestDied      = false;
-    this.HavePlan      = false;
-    this.NoPlan        = false;
-    this.CurrTest      = 0;
-    this.ExpectedTests = 0;
-    this.UseNums       = true;
-    this.NoHeader      = false;
-    this.NoEnding      = false;
-    this.TestResults   = [];
-    this.ToDo          = [];
-    this.Buffer        = [];
-    this.asyncs        = [0];
-    this.asyncID       = 0;
-    return this._setupOutput();
-};
-
-Test.Builder.prototype._print = function (msg) {
-    this.output().call(this, msg);
-};
-
-Test.Builder.prototype.warn = function (msg) {
-    this.warnOutput().apply(this, arguments);
-};
-
-Test.Builder.prototype.plan = function (arg) {
-    if (!arg) return;
-    if (this.HavePlan) Test.Builder.die("You tried to plan twice!");
-
-    if (!(arg instanceof Object))
-        Test.Builder.die("plan() doesn't understand " + arg);
-    for (var cmd in arg) {
-        if (cmd == 'tests') {
-            if (arg[cmd] == null) {
-                TestBulder.die(
-                    "Got an undefined number of tests. Looks like you tried to "
-                    + "say how many tests you plan to run but made a mistake."
-                    + Test.Builder.LF
-                );
-            } else if (!arg[cmd]) {
-                Test.Builder.die(
-                    "You said to run 0 tests! You've got to run something."
-                    + Test.Builder.LF
-                );
-            } else {
-                this.expectedTests(arg[cmd]);
-            }
-        } else if (cmd == 'skipAll') {
-            this.skipAll(arg[cmd]);
-        } else if (cmd == 'noPlan' && arg[cmd]) {
-            this.noPlan();
-        } else {
-            // Do nothing, since Object.prototype might have been changed.
-            // Too bad JS doesn't have real hashes!
-            // Test.Builder.die("plan() doesn't understand "
-            // + cmd + (arg[cmd] ? (" " + arg[cmd]) : ''));
-        }
-    }
-};
-
-Test.Builder.prototype.expectedTests = function (max) {
-    if (max) {
-        if (isNaN(max)) {
-            Test.Builder.die(
-                "Number of tests must be a postive integer. You gave it '"
-                + max + "'." + Test.Builder.LF
-            );
-        }
-
-        this.ExpectedTests = max.valueOf();
-        this.HavePlan       = true;
-        if (!this.noHeader()) this._print("1.." + max + Test.Builder.LF);
-    }
-    return this.ExpectedTests;
-};
-
-Test.Builder.prototype.noPlan = function () {
-    this.NoPlan   = 1;
-    this.HavePlan = 1;
-};
-
-Test.Builder.prototype.hasPlan = function () {
-    if (this.ExpectedTests) return this.ExpectedTests;
-    if (this.NoPlan) return 'noPlan';
-};
-
-Test.Builder.prototype.skipAll = function (reason) {
-    var out = "1..0";
-    if (reason) out += " # Skip " + reason;
-    out += Test.Builder.LF;
-    this.SkipAll = 1;
-    if (!this.noHeader()) this.output()(out);
-    // Just throw and catch an exception.
-    Test.Builder.globalScope.onerror = function () { return true; }
-    throw new Error("__SKIP_ALL__");
-};
-
-Test.Builder.prototype.ok = function (test, desc) {
-    // test might contain an object that we don't want to accidentally
-    // store, so we turn it into a boolean.
-    test = !!test;
-
-    if (!this.HavePlan) {
-        Test.Builder.die(
-            "You tried to run a test without a plan! Gotta have a plan."
-        );
-    }
-
-    // Append any output to the previous test's results.
-    if (this.Buffer.length && this.TestResults.length) {
-        this.TestResults[this.TestResults.length - 1].output +=
-            this.Buffer.splice(0, this.Buffer.length).join('');
-    
-    }
-
-    // I don't think we need to worry about threading in JavaScript.
-    this.CurrTest++;
-
-    // In case desc is a string overloaded object, force it to stringify.
-    if (desc) desc = desc.toString();
-
-    var startsNumber
-    if (desc != null && /^[\d\s]+$/.test(desc)) {
-        this.diag( "Your test description is '" + desc + "'. You shouldn't use",
-                   Test.Builder.LF,
-                   "numbers for your test names. Very confusing.");
-    }
-
-    var todo = this._todo();
-    // I don't think we need to worry about result beeing shared between
-    // threads.
-    var out = '';
-    var result = {};
-
-    if (test) {
-        result.ok        = true;
-        result.actual_ok = test;
-    } else {
-        out += 'not ';
-        result.ok        = todo ? true : false;
-        result.actual_ok = false;
-    }
-
-    out += 'ok';
-    if (this.useNumbers) out += ' ' + this.CurrTest;
-
-    if (desc == null) {
-        result.desc = '';
-    } else {
-        desc = desc.replace(Test.Builder.lineEndingRx, Test.Builder.LF + "# ");
-        // XXX Does this matter since we don't have a TestHarness?
-        desc.split('#').join('\\#'); // # # in a desc can confuse TestHarness.
-        out += ' - ' + desc;
-        result.desc = desc;
-    }
-
-    if (todo) {
-        todo = todo.replace(Test.Builder.lineEndingRx, Test.Builder.LF + "# ");
-        out += " # TODO " + todo;
-        result.reason = todo;
-        result.type   = 'todo';
-    } else {
-        result.reason = '';
-        result.type   = '';
-    }
-
-    this.TestResults[this.CurrTest - 1] = result;
-
-    out += Test.Builder.LF;
-    this._print(out);
-
-    if (!test) {
-        var msg = todo ? "Failed (TODO)" : "Failed";
-        this.diag("    " + msg + " test");
-    }
-    result.output = this.Buffer.splice(0, this.Buffer.length).join('');
-    return test;
-};
-
-Test.Builder.prototype.isEq = function (got, expect, desc) {
-    if (got == null || expect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '==', expect, desc);
-    }
-    return this.cmpOK(got, '==', expect, desc);
-};
-
-Test.Builder.prototype.isNum = function (got, expect, desc) {
-    if (got == null || expect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '==', expect, desc);
-    }
-    return this.cmpOK(Number(got), '==', Number(expect), desc);
-};
-
-Test.Builder.prototype.isntEq = function (got, dontExpect, desc) {
-    if (got == null || dontExpect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '!=', dontExpect, desc);
-    }
-    return this.cmpOK(got, '!=', dontExpect, desc);
-};
-
-Test.Builder.prototype.isntNum = function (got, dontExpect, desc) {
-    if (got == null || dontExpect == null) {
-        // undefined only matches undefined and nothing else
-        return this.isUndef(got, '!=', dontExpect, desc);
-    }
-    return this.cmpOK(Number(got), '!=', Number(dontExpect), desc);
-};
-
-Test.Builder.prototype.like = function (val, regex, desc) {
-    return this._regexOK(val, regex, '=~', desc);
-};
-
-Test.Builder.prototype.unlike = function (val, regex, desc) {
-    return this._regexOK(val, regex, '!~', desc);
-};
-
-Test.Builder.prototype._regexOK = function (val, regex, cmp, desc) {
-    // Create a regex object.
-    var type = Test.Builder.typeOf(regex);
-    var ok;
-    if (type.toLowerCase() == 'string') {
-        // Create a regex object.
-        regex = new RegExp(regex);
-    } else {
-        if (type != 'RegExp') {
-            ok = this.ok(false, desc);
-            this.diag("'" + regex + "' doesn't look much like a regex to me.");
-            return ok;
-        }
-    }
-
-    if (val == null || typeof val != 'string') {
-        if (cmp == '=~') {
-            // The test fails.
-            ok = this.ok(false, desc);
-            this._diagLike(val, regex, cmp);
-        } else {
-            // undefined matches nothing (unlike in Perl, where undef =~ //).
-            ok = this.ok(true, desc);
-        }
-        return ok;
-    }
-
-    // Use val.match() instead of regex.test() in case they've set g.
-    var test = val.match(regex);
-    if (cmp == '!~') test = !test;
-    ok = this.ok(test, desc);
-    if (!ok) this._diagLike(val, regex, cmp);
-    return ok;
-};
-
-Test.Builder.prototype._diagLike = function (val, regex, cmp) {
-    var match = cmp == '=~' ? "doesn't match" : "      matches";
-    return this.diag(
-        "                  '" + val + "" + Test.Builder.LF +
-        "    " + match + " /" + regex.source + "/"
-    );
-};
-
-Test.Builder.prototype.cmpOK = function (got, op, expect, desc) {
-
-    var test;
-    if (Test.Builder.StringOps[op]) {
-        // Force string context.
-        test = eval("got.toString() " + Test.Builder.StringOps[op] + " expect.toString()");
-    } else {
-        test = eval("got " + op + " expect");
-    }
-
-    var ok = this.ok(test, desc);
-    if (!ok) {
-        if (/^(eq|==)$/.test(op)) {
-            this._isDiag(got, op, expect);
-        } else {
-            this._cmpDiag(got, op, expect);
-        }
-    }
-    return ok;
-};
-
-Test.Builder.prototype._cmpDiag = function (got, op, expect) {
-    if (got != null) got = "'" + got.toString() + "'";
-    if (expect != null) expect = "'" + expect.toString() + "'";
-    return this.diag("    " + got + Test.Builder.LF + "        " + op
-                     + Test.Builder.LF + "    " + expect);
-};
-
-Test.Builder.prototype._isDiag = function (got, op, expect) {
-    var args = [got, expect];
-    for (var i = 0; i < args.length; i++) {
-        if (args[i] != null) {
-            args[i] = op == 'eq' ? "'" + args[i].toString() + "'" : args[i].valueOf();
-
-            args[i] = args[i].replace(/&/g, '&amp;'); 
-            args[i] = args[i].replace(/</g, '&lt;');
-            args[i] = args[i].replace(/>/g, '&gt;');
-            args[i] = args[i].replace(/"/g, '&quot;'); // " end quote for emacs
-        }
-    }
-
-
-    return this.diag(
-        "         got: \"" + args[0] + "\"" + Test.Builder.LF +
-        "    expected: \"" + args[1] + "\"" + Test.Builder.LF
-    );
-};
-
-Test.Builder.prototype.BAILOUT = function (reason) {
-    this._print("Bail out! " + reason);
-    // Just throw and catch an exception.
-    Test.Builder.globalScope.onerror = function () {
-        // XXX Do something to tell TestHarness it was a bailout?
-        return true;
-    }
-    throw new Error("__BAILOUT__");
-};
-
-Test.Builder.prototype.skip = function (why) {
-    if (!this.HavePlan)
-        Test.Builder.die("You tried to run a test without a plan! Gotta have a plan.");
-
-    // In case desc is a string overloaded object, force it to stringify.
-    if (why) why = why.toString().replace(Test.Builder.lineEndingRx,
-                                          Test.Builder.LF+ "# ");
-
-    this.CurrTest++;
-    this.TestResults[this.CurrTest - 1] = {
-        ok:        true,
-        actual_ok: true,
-        desc:      '',
-        type:      'skip',
-        reason:    why
-    };
-
-    var out = "ok";
-    if (this.useNumbers) out += ' ' + this.CurrTest;
-    out    += " # skip " + why + Test.Builder.LF;
-    this._print(out);
-    this.TestResults[this.CurrTest - 1].output =
-    this.Buffer.splice(0, this.Buffer.length).join('');
-    return true;
-};
-
-Test.Builder.prototype.todoSkip = function (why) {
-    if (!this.HavePlan)
-        Test.Builder.die("You tried to run a test without a plan! Gotta have a plan.");
-
-    // In case desc is a string overloaded object, force it to stringify.
-    if (why) why = why.toString().replace(Test.Builder.lineEndingRx,
-                                          Test.Builder.LF + "# ");
-    
-
-    this.CurrTest++;
-    this.TestResults[this.CurrTest - 1] = {
-        ok:        true,
-        actual_ok: false,
-        desc:      '',
-        type:      'todo_skip',
-        reason:    why
-    };
-
-    var out = "not ok";
-    if (this.useNumbers) out += ' ' + this.CurrTest;
-    out    += " # TODO & SKIP " + why + Test.Builder.LF;
-    this._print(out);
-    this.TestResults[this.CurrTest - 1].output =
-    this.Buffer.splice(0, this.Buffer.length).join('');
-    return true;
-};
-
-Test.Builder.prototype.skipRest = function (reason) {
-    var out = "# Skip";
-    if (reason) out += " " + reason;
-    out += Test.Builder.LF;
-    if (this.NoPlan) this.skip(reason);
-    else {
-        for (var i = this.CurrTest; i < this.ExpectedTests; i++) {
-            this.skip(reason);
-        }
-    }
-    // Just throw and catch an exception.
-    Test.Builder.globalScope.onerror = function () { return true; }
-    throw new Error("__SKIP_REST__");
-};
-
-Test.Builder.prototype.useNumbers = function (useNums) {
-    if (useNums != null) this.UseNums = useNums;
-    return this.UseNums;
-};
-
-Test.Builder.prototype.noHeader = function (noHeader) {
-    if (noHeader != null) this.NoHeader = !!noHeader;
-    return this.NoHeader;
-};
-
-Test.Builder.prototype.noEnding = function (noEnding) {
-    if (noEnding != null) this.NoEnding = !!noEnding;
-    return this.NoEnding;
-};
-
-Test.Builder.prototype.diag = function () {
-    if (!arguments.length) return;
-
-    var msg = '# ';
-    // Join each agument and escape each line with a #.
-    for (var i = 0; i < arguments.length; i++) {
-        // Replace any newlines.
-        msg += arguments[i].toString().replace(Test.Builder.lineEndingRx,
-                                               Test.Builder.LF + "# ");
-    }
-
-    // Append a new line to the end of the message if there isn't one.
-    if (!(new RegExp(Test.Builder.LF + '$').test(msg)))
-        msg += Test.Builder.LF;
-    // Append the diag message to the most recent result.
-    return this._printDiag(msg);
-};
-
-Test.Builder.prototype._printDiag = function () {
-    var fn = this.todo() ? this.todoOutput() : this.failureOutput();
-    fn.apply(this, arguments);
-    return false;
-};
-
-Test.Builder.prototype.output = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.Output = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.Output;
-};
-
-Test.Builder.prototype.failureOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.FailureOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.FailureOutput;
-};
-
-Test.Builder.prototype.todoOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.TodoOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.TodoOutput;
-};
-
-Test.Builder.prototype.endOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.EndOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.EndOutput;
-};
-
-Test.Builder.prototype.warnOutput = function (fn) {
-    if (fn != null) {
-        var buffer = this.Buffer;
-        this.WarnOutput = function (msg) { buffer.push(msg); fn(msg) };
-    }
-    return this.WarnOutput;
-};
-
-Test.Builder.prototype._setupOutput = function () {
-    if (Test.PLATFORM == 'browser') {
-        var top = Test.Builder.globalScope;
-        var doc = top.document;
-        var writer = function (msg) {
-            // I'm sure that there must be a more efficient way to do this,
-            // but if I store the node in a variable outside of this function
-            // and refer to it via the closure, then things don't work right
-            // --the order of output can become all screwed up (see
-            // buffer.html).  I have no idea why this is.
-            var node = doc.getElementById("test");
-            var body = doc.body || doc.getElementsByTagName("body")[0];
-            if (node) {
-                // This approach is neater, but causes buffering problems when
-                // mixed with document.write. See tests/buffer.html.
-                //node.appendChild(document.createTextNode(msg));
-                //return;
-                for (var i = 0; i < node.childNodes.length; i++) {
-                    if (node.childNodes[i].nodeType == 3 /* Text Node */) {
-                        // Append to the node and scroll down.
-                        node.childNodes[i].appendData(msg);
-                        top.scrollTo(
-                            0, body.offsetHeight || body.scrollHeight
-                        );
-                        return;
-                    }
-                }
-
-                // If there was no text node, add one.
-                node.appendChild(doc.createTextNode(msg));
-                top.scrollTo(0, body.offsetHeight || body.scrollHeight);
-                return;
-            }
-
-            // Default to the normal write and scroll down...
-            doc.write("<pre style='margin: 0px'>"+msg+"</pre>");
-            // IE 6 Service Pack 2 requires that we re-cache the object. Bill
-            // Gates only knows why!
-            body = doc.body || doc.getElementsByTagName("body")[0];
-            if (body) top.scrollTo(0, body.offsetHeight || body.scrollHeight);
-        };
-
-        this.output(writer);
-        this.failureOutput(function (msg) {
-            writer('<span style="color: red; font-weight: bold">'
-                   + msg + '</span>')
-        });
-        this.todoOutput(writer);
-        this.endOutput(writer);
-
-        if (top.alert.apply) {
-            this.warnOutput(top.alert, top);
-        } else {
-            this.warnOutput(function (msg) { top.alert(msg); });
-        }
-
-    } else if (Test.PLATFORM == 'director') {
-        // Macromedia-Adobe:Director MX 2004 Support
-        // XXX Is _player a definitive enough object?
-        // There may be an even more explicitly Director object.
-        this.output(trace);       
-        this.failureOutput(trace);
-        this.todoOutput(trace);
-        this.warnOutput(trace);
-    }
-
-    return this;
-};
-
-Test.Builder.prototype.currentTest = function (num) {
-    if (num == null) return this.CurrTest;
-
-    if (!this.HavePlan)
-        Test.Builder.die("Can't change the current test number without a plan!");
-    this.CurrTest = num;
-    if (num > this.TestResults.length ) {
-        var reason = 'incrementing test number';
-        for (var i = this.TestResults.length; i < num; i++) {
-            this.TestResults[i] = {
-                ok:        true, 
-                actual_ok: null,
-                reason:    reason,
-                type:      'unknown', 
-                name:      null,
-                output:    'ok - ' + reason + Test.Builder.LF
-            };
-        }
-    } else if (num < this.TestResults.length) {
-        // IE requires the second argument to truncate the array.
-        this.TestResults.splice(num, this.TestResults.length);
-    }
-    return this.CurrTest;
-};
-
-Test.Builder.prototype.summary = function () {
-    var results = new Array(this.TestResults.length);
-    for (var i = 0; i < this.TestResults.length; i++) {
-        results[i] = this.TestResults[i]['ok'];
-    }
-    return results
-};
-
-Test.Builder.prototype.details = function () {
-    return this.TestResults;
-};
-
-Test.Builder.prototype.todo = function (why, howMany) {
-    if (howMany) this.ToDo = [why, howMany];
-    return this.ToDo[1];
-};
-
-Test.Builder.prototype._todo = function () {
-    if (this.ToDo[1]) {
-        if (this.ToDo[1]--) return this.ToDo[0];
-        this.ToDo = [];
-    }
-    return false;
-};
-
-Test.Builder.prototype._sanity_check = function () {
-    Test.Builder._whoa(
-        this.CurrTest < 0,
-        'Says here you ran a negative number of tests!'
-    );
-
-    Test.Builder._whoa(
-        !this.HavePlan && this.CurrTest, 
-        'Somehow your tests ran without a plan!'
-    );
-
-    Test.Builder._whoa(
-        this.CurrTest != this.TestResults.length,
-        'Somehow you got a different number of results than tests ran!'
-    );
-};
-
-Test.Builder.prototype._notifyHarness = function () {
-    var top = Test.Builder.globalScope;
-    // Special treatment for the browser harness.
-    if (top.parent && top.parent.Test && top.parent.Test.Harness) {
-        top.parent.Test.Harness.Done++;
-    }
-};
-
-Test.Builder.prototype._ending = function () {
-    if (this.Ended) return;
-    this.Ended = true;
-    if (this.noEnding()) {
-        this._notifyHarness();
-        return;
-    }
-    this._sanity_check();
-    var out = this.endOutput();
-
-    // Figure out if we passed or failed and print helpful messages.
-    if( this.TestResults.length ) {
-        // The plan?  We have no plan.
-        if (this.NoPlan) {
-            if (!this.noHeader())
-                this._print("1.." + this.CurrTest + Test.Builder.LF);
-            this.ExpectedTests = this.CurrTest;
-        }
-
-        var numFailed = 0;
-        for (var i = 0; i < this.TestResults.length; i++) {
-            if (!this.TestResults[i]) numFailed++;
-        }
-        numFailed += Math.abs(
-            this.ExpectedTests - this.TestResults.length
-        );
-
-        if (this.CurrTest < this.ExpectedTests) {
-            var s = this.ExpectedTests == 1 ? '' : 's';
-            out(
-                "# Looks like you planned " + this.ExpectedTests + " test"
-                + s + " but only ran " + this.CurrTest + "." + Test.Builder.LF
-            );
-        } else if (this.CurrTest > this.ExpectedTests) {
-           var numExtra = this.CurrTest - this.ExpectedTests;
-            var s = this.ExpectedTests == 1 ? '' : 's';
-            out(
-                "# Looks like you planned " + this.ExpectedTests + " test"
-                + s + " but ran " + numExtra + " extra." + Test.Builder.LF
-            );
-        } else if (numFailed) {
-            var s = numFailed == 1 ? '' : 's';
-            out(
-                "# Looks like you failed " + numFailed + "test" + s + " of "
-                + this.ExpectedTests + "." + Test.Builder.LF
-            );
-        }
-
-        if (this.TestDied) {
-            out(
-                "# Looks like your test died just after " 
-                + this.CurrTest + "." + Test.Builder.LF
-            );
-        }
-
-    } else if (!this.SkipAll) {
-        // skipAll requires no status output.
-        if (this.TestDied) {
-            out(
-                "# Looks like your test died before it could output anything."
-                + Test.Builder.LF
-            );
-        } else {
-            out("# No tests run!" + Test.Builder.LF);
-        }
-    }
-    this._notifyHarness();
-};
-
-Test.Builder.prototype.isUndef = function (got, op, expect, desc) {
-    // Undefined only matches undefined, so we don't need to cast anything.
-    var test = eval("got " + (Test.Builder.StringOps[op] || op) + " expect");
-    this.ok(test, desc);
-    if (!test) this._isDiag(got, op, expect);
-    return test;
-};
-
-Test.Builder._finish = function (pkg) {
-    if (!pkg) pkg = Test;
-    for (var i = 0; i < pkg.Builder.Instances.length; i++) {
-        // The main process is always async ID 0.
-        if (!pkg.Builder.Instances[i].Ended)
-            pkg.Builder.Instances[i].endAsync(0);
-    }
-};
-
-if (Test.Builder.globalScope) {
-    // Set up an onload function to end all tests.
-    Test.Builder.globalScope.onload = function (event, pkg) {
-        // The package may be passed in if onload() is called explicitly.
-        // This is to get around a very weird scoping bug in my version of
-        // Firefox. See Test.Harness.Browser.runTest() for this usage.
-        Test.Builder._finish(pkg)
-    };
-
-    // Set up an exception handler. This is so that we can capture deaths but
-    // still output information for TestHarness to pick up.
-    Test.Builder.globalScope.onerror = function (msg, url, line) {
-        // Output the exception.
-        Test.Builder.Test.TestDied = true;
-        Test.Builder.Test.diag("Error in " + url + " at line " + line + ": " + msg);
-        return true;
-    };
-};
-
-Test.Builder.prototype.beginAsync = function (timeout) {
-	var id = ++this.asyncID;
-    var top = Test.Builder.globalScope;
-    if (timeout && top && top.setTimeout) {
-        // Are there other ways of setting timeout in non-browser settings?
-        var aTest = this;
-        this.asyncs[id] = top.setTimeout(
-            function () { aTest.endAsync(id) }, timeout
-        );
-    } else {
-        // Make sure it's defined.
-        this.asyncs[id] = 0;
-    }
-	return id;
-};
-
-Test.Builder.prototype.endAsync = function (id) {
-    if (this.asyncs[id] == undefined) return;
-    if (this.asyncs[id]) {
-		// Remove the timeout
-		Test.Builder.globalScope.clearTimeout(this.asyncs[id]);
-	}
-    if (--this.asyncID < 0) this._ending();
-};
-
-Test.Builder.exporter = function (pkg, root) {
-    if (typeof root == 'undefined') {
-        root = Test.Builder.globalScope;
-        if (!root) throw new Error("Platform unknown");
-    }
-    for (var i = 0; i < pkg.EXPORT.length; i++) {
-        if (typeof root[pkg.EXPORT[i]] == 'undefined')
-            root[pkg.EXPORT[i]] = pkg[pkg.EXPORT[i]];
-    }
-};
@@ -1,363 +0,0 @@
-// # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
-
-if (typeof JSAN != 'undefined') JSAN.use('Test.Harness');
-else {
-    if (typeof Test == 'undefined') Test = {};
-    if (!Test.Harness) Test.Harness = {};
-}
-
-if (window.parent != window &&
-    location.href.replace(/[?#].+/, "") == parent.location.href.replace(/[?#].+/, ""))
-{
-
-    // Build fake T.H.B so original script from this file doesn't throw
-    // exception. This is a bit of a hack...
-    Test.Harness.Browser = function() {
-        this.runTests = function() {},
-        this.encoding = function () { return this }
-    };
-
-    // We're in a test iframe. Set up the necessary parts and load the
-    // test script with XMLHttpRequest (to support Safari and Opera).
-    var __MY = {};
-    __MY.pre = document.createElement("pre");
-    __MY.pre.id = "test";
-    if (window.parent.Test.Harness.Browser._encoding) {
-        // Set all scripts to use the appropriate encoding.
-        __MY.scripts = document.getElementsByTagName('script');
-        for (var j = 0; j < __MY.scripts.length; j++) {
-            __MY.scripts[j].charset =
-                window.parent.Test.Harness.Browser._encoding;
-        }
-    }
-
-    // XXX replace with a script element at some point? Safari is due to
-    // have this working soon (not sure about IE or Opera):
-    // http://bugzilla.opendarwin.org/show_bug.cgi?id=3748
-    __MY.inc = window.parent.Test.Harness.Browser.includes;
-    __MY.req = typeof XMLHttpRequest != 'undefined'
-      ? new XMLHttpRequest()
-      : new ActiveXObject("Microsoft.XMLHTTP");
-
-    for (var k = 0; k < __MY.inc.length; k++) {
-        __MY.req.open("GET", __MY.inc[k], false);
-        __MY.req.send(null);
-        var stat = __MY.req.status;
-        //           OK   Not Modified    IE Cached   Safari cached
-        if (stat == 200 || stat == 304 || stat == 0 || stat == null) {
-            eval(__MY.req.responseText);
-        } else {
-            throw new Error(
-                "Unable to load " + __MY.inc[k]
-                + ': Status ' + __MY.req.status
-            );
-        }
-    }
-
-    // IE 6 SP 2 doesn't seem to run the onload() event, so we force the
-    // issue.
-    Test.Builder._finish(Test);
-
-    // XXX Opera throws a DOM exception here, but I don't know what to do
-    // about that.
-    __MY.body = document.body
-        || document.getElementsByTagName("body")[0].appendChild(__MY.pre);
-    if (__MY.body) __MY.body.appendChild(__MY.pre);
-    else if (document.appendChild) document.appendChild(__MY.pre);
-
-} else {
-    Test.Harness.Browser = function () {
-        this.includes = Test.Harness.Browser.includes = [];
-        Array.prototype.push.apply(Test.Harness.Browser.includes, arguments);
-        this.includes.push('');
-    };
-
-    Test.Harness.Browser.VERSION = '0.21';
-
-    Test.Harness.Browser.runTests = function () {
-        var harness = new Test.Harness.Browser();
-        harness.runTests.apply(harness, arguments);
-    };
-
-    Test.Harness.Browser.prototype = new Test.Harness();
-    Test.Harness.Browser.prototype.interval = 100;
-
-    Test.Harness.Browser.prototype._setupFrame = function () {
-        // Setup the iFrame to run the tests.
-        var node = document.getElementById('buffer');
-        if (node) return node.contentWindow || frames.buffer;
-        node = document.createElement("iframe");
-        node.setAttribute("id", "buffer");
-        node.setAttribute("name", "buffer");
-        // Safari makes it impossible to do anything with the iframe if it's
-        // set to display:none. See:
-        // http://www.quirksmode.org/bugreports/archives/2005/02/hidden_iframes.html
-        //alert(navigator.userAgent);
-        if (/Safari|Konqueror/.test(navigator.userAgent)) {
-            node.style.visibility = "hidden";
-            node.style.height = "0"; 
-            node.style.width = "0";
-        } else
-            node.style.display = "none";
-        document.body.appendChild(node);
-        return node.contentWindow || frames.buffer;
-    };
-
-    Test.Harness.Browser.prototype._setupOutput = function () {
-        // Setup the pre element for test output.
-        var node = document.createElement("pre");
-        node.setAttribute("id", "output");
-        document.body.appendChild(node);
-        // Brutal hack to make output linkable
-        fixoutput = function(node) {
-            // Trailing space added and replaced to work around yet another
-            // Safari bug.
-            node.innerHTML = node.innerHTML.replace(
-                / ?(\w[\w\.]+?\w)(?=\.\.\.)/m, '<a href="$1">$1</a>'
-            ) + ' ';
-        };
-        return {
-            pass: function (msg) {
-                node.appendChild(document.createTextNode(msg));
-                window.scrollTo(0, document.body.offsetHeight
-                                || document.body.scrollHeight);
-                fixoutput(node);
-            },
-            fail: function (msg) {
-                var red = document.createElement("span");
-                red.setAttribute("style", "color: red; font-weight: bold");
-                node.appendChild(red);
-                red.appendChild(document.createTextNode(msg));
-                window.scrollTo(0, document.body.offsetHeight
-                                || document.body.scrollHeight);
-            }
-        };
-    };
-
-    Test.Harness.Browser.prototype._setupSummary = function () {
-        // Setup the div for the summary.
-        var node = document.createElement("div");
-        node.setAttribute("id", "summary");
-        node.setAttribute(
-            "style", "white-space:pre; font-family: Verdana,Arial,serif;"
-        );
-        document.body.appendChild(node);
-        return function (msg) {
-            node.appendChild(document.createTextNode(msg));
-            window.scrollTo(0, document.body.offsetHeight
-                            || document.body.scrollHeight);
-        };
-};
-
-    Test.Harness.Browser.prototype.runTests = function () {
-        Test.Harness.Browser._encoding = this.encoding();
-        var files = this.args.file
-        ? typeof this.args.file == 'string' ? [this.args.file] : this.args.file
-        : arguments;
-        if (!files.length) return;
-        var outfiles = this.outFileNames(files);
-        var buffer = this._setupFrame();
-        var harness = this;
-        var ti = 0;
-        var start;
-        var output = this._setupOutput();
-        var summaryOutput = this._setupSummary();
-        // These depend on how we're watching for a test to finish.
-        var finish = function () {}, runNext = function () {};
-
-        // This function handles most of the work of outputting results and
-        // running the next test, if there is one.
-        var runner = function () {
-            harness.outputResults(
-                buffer.Test.Builder.Test,
-                files[ti],
-                output,
-                harness.args
-            );
-
-            if (files[++ti]) {
-                output.pass(
-                    outfiles[ti]
-                    + (harness.args.verbose ? Test.Harness.LF : '')
-                );
-                harness.runTest(files[ti], buffer);
-                runNext();
-            } else {
-                harness.outputSummary(summaryOutput, new Date() - start);
-                finish();
-            }
-        };
-
-        if (Object.watch) {
-            // We can use the cool watch method, and avoid setting timeouts!
-            // We just need to unwatch() when all tests are finished.
-            finish = function () { Test.Harness.unwatch('Done') };
-            Test.Harness.watch('Done', function (attr, prev, next) {
-                if (next < buffer.Test.Builder.Instances.length) return next;
-                runner();
-                return 0;
-            });
-        } else {
-            // Damn. We have to set timeouts. :-(
-            var pkg;
-            var wait = function () {
-                // Check Test.Harness.Done. If it's non-zero, then we know
-                // that the buffer is fully loaded, because it has incremented
-                // Test.Harness.Done. Grrr.. IE 6 SP 2 seems to delete
-                // buffer.Test after all the tests have finished running, but
-                // before this code executes for the correct number of
-                // completed tests. So we cache it in a variable outside of
-                // the function on previous calls to the function.
-                if (!pkg) pkg = buffer.Test;
-                if (Test.Harness.Done > 0
-                    && Test.Harness.Done >= pkg.Builder.Instances.length)
-                {
-                    Test.Harness.Done = 0;
-                    // Avoid race condition by resetting the instances, too. I
-                    // have no idea why this might remain set from a previous
-                    // test, but such can be the case in IE 6 SP 2.
-                    pkg.Builder.Instances = [];
-                    runner();
-                } else {
-                    window.setTimeout(wait, harness.interval);
-                }
-            };
-            // We'll just have to set a timeout for the next test.
-            runNext = function () {
-                window.setTimeout(wait, harness.interval);
-            };
-            window.setTimeout(wait, this.interval);
-        }
-
-        // Now start the first test.
-        output.pass(outfiles[ti] + (this.args.verbose ? Test.Harness.LF : ''));
-        start = new Date();
-        this.runTest(files[ti], buffer);
-    };
-
-    Test.Harness.Browser.prototype.runTest = function (file, buffer) {
-        if (/\.html$/.test(file)) {
-            buffer.location.replace(file);
-        } else { // if (/\.js$/.test(file)) {
-            if (/MSIE/.test(navigator.userAgent)
-                || /Opera/.test(navigator.userAgent)
-                || /Safari|Konqueror/.test(navigator.userAgent))
-            {
-                // These browsers have problems with the DOM solution. It
-                // simply doesn't work in Safari, and Opera considers its
-                // handling of buffer.document to be a security violation. So
-                // have them use the XML hack, instead.
-                this.includes[this.includes.length-1] = file;
-                buffer.location.replace(location.pathname + "?xml-hack=1");
-                return;
-            }
-            // document.write() simply doesn't work here. Thanks to
-            // Pawel Chmielowski for figuring that out!
-            var doc = buffer.document;
-            doc.open("text/html");
-            doc.close();
-            var el;
-
-            // XXX Opera chokes on this line. It thinks that using the doc
-            // element like this is a security violation, never mind that we
-            // were the ones who actually created it. Whatever!
-            var body = doc.body || doc.getElementsByTagName("body")[0];
-            var head = doc.getElementsByTagName("head")[0];
-
-            // Safari seems to be headless at this point.
-            if (!head) {
-                head = doc.createElement('head');
-                doc.appendChild(head);
-            }
-
-            // Add script elements for all includes.
-            for (var i = 0; i < this.includes.length - 1; i++) {
-                el = doc.createElement("script");
-                el.setAttribute("src", this.includes[i]);
-                head.appendChild(el);
-            }
-
-
-            // Create the pre and script element for the test file.
-            var pre = doc.createElement("pre");
-            pre.id = "test";
-            el = doc.createElement("script");
-            el.type = "text/javascript";
-            if (this.encoding()) el.charset = this.encoding();
-
-            // XXX This doesn't work in Safari right now. See
-            // http://bugzilla.opendarwin.org/show_bug.cgi?id=3748
-            el.src = file;
-            pre.appendChild(el);
-
-            // Create a script element to finish the tests.
-            el = doc.createElement("script");
-            el.type = "text/javascript";
-            var text = "window.onload(null, Test)";
-
-            // IE doesn't let script elements have children.
-            if (null != el.canHaveChildren) el.text = text;
-            // But most other browsers do.
-            else el.appendChild(document.createTextNode(text));
-
-            pre.appendChild(el);
-
-            // IE 6 SP 2 Requires getting the body element again.
-            body = doc.body || doc.getElementsByTagName("body")[0];
-            body.appendChild(pre);
-        /* Let's just assume that if it's not .html, it's JavaScript.
-        } else {
-            // Who are you, man??
-            alert("I don't know what kind of file '" + file + "' is");
-        */
-        }
-    };
-
-    Test.Harness.Browser.prototype.args = {};
-    var pairs = location.search.substring(1).split(/[;&]/);
-    for (var i = 0; i < pairs.length; i++) {
-        var parts = pairs[i].split('=');
-        if (parts[0] == null) continue;
-        var key = unescape(parts[0]), val = unescape(parts[1]);
-        if (Test.Harness.Browser.prototype.args[key] == null) {
-            Test.Harness.Browser.prototype.args[key] = unescape(val);
-        } else {
-            if (typeof Test.Harness.Browser.prototype.args[key] == 'string') {
-                Test.Harness.Browser.prototype.args[key] =
-                    [Test.Harness.Browser.prototype.args[key], unescape(val)];
-            } else {
-                Test.Harness.Browser.prototype.args[key].push(unescape(val));
-            }
-        }
-    }
-    delete pairs;
-
-    Test.Harness.Browser.prototype.formatFailures = function (fn) {
-        // XXX Switch to DOM?
-        var failedStr = "Failed Test";
-        var middleStr = " Total Fail  Failed  ";
-        var listStr = "List of Failed";
-        var table =
-            '<style>table {padding: 0; border-collapse: collapse; }'
-          + 'tr { height: 2em; }'
-          + 'th { background: lightgrey; }'
-          + 'td, th { padding: 2px 5px; text-align: left; border: solid #000000 1px;}'
-          + '.odd { background: #e8e8cd }'
-          + '</style>'
-          + '<table style="padding: 0"><tr><th>Failed Test</th><th>Total</th>'
-          + '<th>Fail</th><th>Failed</th></tr>';
-        for (var i = 0; i < this.failures.length; i++) {
-            var track = this.failures[i];
-            var style = i % 2 ? 'even' : 'odd';
-            table += '<tr class="' + style + '"><td>' + track.fn + '</td>'
-              + '<td>' + track.total + '</td>'
-              + '<td>' + (track.total - track.ok) + '</td>'
-              + '<td>' + this._failList(track.failList) + '</td></tr>';
-        };
-        table += '</table>' + Test.Harness.LF;
-        var node = document.getElementById('summary');
-        node.innerHTML += table;
-        window.scrollTo(0, document.body.offsetHeight
-                        || document.body.scrollHeight);
-    };
-}
@@ -1,277 +0,0 @@
-// # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
-
-// Set up namespace.
-if (typeof self != 'undefined') {
-    //Browser
-    if (typeof Test == 'undefined') Test = {PLATFORM: 'browser'};
-    else Test.PLATFORM = 'browser';
-} else if (typeof _player != 'undefined'){
-    //Director
-    if (typeof _global.Test != "object") _global.Test = {PLATFORM: 'director'};
-    else _global.Test.PLATFORM = 'director';
-} else {
-    throw new Error("Test.Harness does not support your platform");
-}
-
-Test.Harness = function () {};
-Test.Harness.VERSION = '0.21';
-Test.Harness.Done = 0;
-
-// Stoopid IE.
-Test.Harness.LF = typeof navigator != "undefined"
-    && navigator.userAgent.toLowerCase().indexOf('msie') + 1
-    && ( ( typeof JSAN != "undefined" && JSAN.globalScope.opera == undefined )
-         || ( Test.PLATFORM == 'browser' && window.opera == undefined ) )
-  ? "\r"
-  : "\n";
-
-Test.Harness.prototype.isDone = Test.Harness.isDone;
-
-/*
-
-    bonus           Number of individual todo tests unexpectedly passed
-    ran             Number of individual tests ran
-    ok              Number of individual tests passed
-    subSkipped      Number of individual tests skipped
-    todo            Number of individual todo tests
-
-    files           Number of test files ran
-    good            Number of test files passed
-    bad             Number of test files failed
-    tests           Number of test files originally given
-    skipped         Number of test files skipped
-
-*/
-
-Test.Harness.prototype.bonus      = 0;
-Test.Harness.prototype.ran        = 0;
-Test.Harness.prototype.ok         = 0;
-Test.Harness.prototype.subSkipped = 0;
-Test.Harness.prototype.todo       = 0;
-Test.Harness.prototype.files      = 0;
-Test.Harness.prototype.good       = 0;
-Test.Harness.prototype.bad        = 0;
-Test.Harness.prototype.tests      = 0;
-Test.Harness.prototype.skipped    = 0;
-Test.Harness.prototype.failures   = [];
-Test.Harness.prototype._encoding  = null;
-
-Test.Harness.prototype.encoding = function (enc) {
-    if (!enc) return this._encoding;
-    this._encoding = enc;
-    return this;
-};
-
-Test.Harness.runTests = function () {
-    // XXX Can't handle inheritance, right? Or can we?
-    var harness = new Test.Harness();
-    harness.runTests.apply(harness, arguments);
-};
-
-Test.Harness.prototype.outFileNames = function (files) {
-    var len = 0;
-    for (var i = 0; i < files.length; i++) {
-        if (files[i].length > len) len = files[i].length;
-    }
-    len += 3;
-    var ret = [];
-    for (var i = 0; i < files.length; i++) {
-        var outName = files[i];
-        var add = len - files[i].length;
-        // Where is Perl's x operator when I need it??
-        for (var j = 0; j < add; j++) {
-            outName += '.';
-        }
-        ret.push(outName);
-    }
-    return ret;
-};
-
-Test.Harness.prototype.outputResults = function (test, file, out, attrs) {
-    this.tests++;
-    this.ran += test.TestResults.length;
-    var fails = [];
-    var track = {
-        fn:       file,
-        total:    test.expectedTests(),
-        ok:       0,
-        failList: []
-    };
-
-    if (test.TestResults.length) {
-        this.files++;
-        var pass = true;
-        for (var i = 0; i < test.TestResults.length; i++) {
-            // Start out assuming passage.
-            if (test.TestResults[i].ok) {
-                if (attrs.verbose) out.pass(test.TestResults[i].output);
-                this.ok++;
-                track.ok++
-                if (test.TestResults[i].type == 'todo') {
-                    // Handle unexpected pass.
-                    if (test.TestResults[i].actualOK) this.bonus++;
-                    this.todo ++;
-                } else if (test.TestResults[i].type == 'skip') this.subSkipped++;
-            } else {
-                if (test.TestResults[i].type == 'todo') {
-                    // Expected failure.
-                    this.todo++;
-                    if (attrs.verbose) out.pass(test.TestResults[i].output);
-                } else {
-                    pass = false;
-                    track.failList.push(i + 1);
-                    out.fail(test.TestResults[i].output);
-                }
-            }
-            
-        }
-        
-        if (pass) {
-            this.good++;
-            out.pass("ok" + Test.Harness.LF);
-        } else {
-            this.bad++;
-            var err = "NOK # Failed ";
-            if (track.failList.length == 1) {
-                err += "test " + track.failList[0];
-            } else {
-                err += "tests " + this._failList(track.failList);
-            }
-            out.fail(err + " in " + file + Test.Harness.LF);
-        }
-    } else if (test.SkipAll){
-        // All tests skipped.
-        this.skipped++;
-        this.good++;
-        out.pass(test.Buffer.join('').replace(/[^#]+#\s+Skip /, 'all skipped: '));
-    } else {
-        // Wha happened? Tests ran, but no results!
-        this.files++;
-        this.bad++;
-        out.fail("FAILED before any test output arrived" + Test.Harness.LF);
-    }
-    if (track.failList.length) this.failures.push(track);
-};
-
-Test.Harness.prototype._allOK = function () {
-    return this.bad == 0 && (this.ran || this.skipped) ? true : false;
-};
-
-Test.Harness.prototype.outputSummary = function (fn, time) {
-    var bonusmsg = this._bonusmsg();
-    var pct;
-    if (this._allOK()) {
-        fn("All tests successful" + bonusmsg + '.' + Test.Harness.LF);
-    } else if (!this.tests) {
-        fn("FAILED—no tests were run for some reason." + Test.Harness.LF);
-    } else if (!this.ran) {
-        var blurb = this.tests == 1 ? "file" : "files";
-        fn("FAILED—" + this.tests + " test " + blurb + " could be run, "
-           + "alas—no output ever seen." + Test.Harness.LF);
-    } else {
-        pct = this.good / this.tests * 100;
-        var pctOK = 100 * this.ok / this.ran;
-        var subpct = (this.ran - this.ok) + "/" + this.ran
-          + " subtests failed, " + pctOK.toPrecision(4) + "% okay.";
-
-        if (this.bad) {
-            bonusmsg = bonusmsg.replace(/^,?\s*/, '');
-            if (bonusmsg) fn(bonusmsg + '.' + Test.Harness.LF);
-            fn("Failed " + this.bad + "/" + this.tests + " test scripts, "
-               + pct.toPrecision(4) + "% okay. " + subpct + Test.Harness.LF);
-        }
-        this.formatFailures(fn);
-    }
-
-    fn("Files=" + this.tests + ", Tests=" + this.ran + ", " + (time / 1000)
-       + " seconds" + Test.Harness.LF);
-};
-
-Test.Harness.prototype.formatFailures = function () {
-    var table = '';
-    var failedStr = "Failed Test";
-    var middleStr = " Total Fail  Failed  ";
-    var listStr = "List of Failed";
-    var cols = 80;
-
-    // Figure out our longest name string for formatting purposes.
-    var maxNamelen = failedStr.length;
-    for (var i = 0; i < this.failures.length; i++) {
-        var len = this.failures[i].length;
-        if (len > maxNamelen) maxNamelen = len;
-    }
-
-    var listLen = cols - middleStr.length - maxNamelen.length;
-    if (listLen < listStr.length) {
-        listLen = listStr.length;
-        maxNamelen = cols - middleStr.length - listLen;
-        if (maxNamelen < failedStr.length) {
-            maxNamelen = failedStr.length;
-            cols = maxNamelen + middleStr.length + listLen;
-        }
-    }
-
-    var out = failedStr;
-    if (out.length < maxNamelen) {
-        for (var j = out.length; j < maxNameLength; j++) {
-            out += ' ';
-        }
-    }
-    out += '  ' + middleStr;
-    // XXX Need to finish implementing the text-only version of the failures
-    // table.
-};
-
-Test.Harness.prototype._bonusmsg = function () {
-    var bonusmsg = '';
-    if (this.bonus) {
-        bonusmsg = (" (" + this.bonus + " subtest" + (this.bonus > 1 ? 's' : '')
-          + " UNEXPECTEDLY SUCCEEDED)");
-    }
-
-    if (this.skipped) {
-        bonusmsg += ", " + this.skipped + " test"
-          + (this.skipped != 1 ? 's' : '');
-        if (this.subSkipped) {
-            bonusmsg += " and " + this.subSkipped + " subtest"
-              + (this.subSkipped != 1 ? 's' : '');
-        }
-        bonusmsg += ' skipped';
-    } else if (this.subSkipped) {
-        bonusmsg += ", " + this.subSkipped + " subtest"
-          + (this.subSkipped != 1 ? 's' : '') + " skipped";
-    }
-
-    return bonusmsg;
-}
-
-Test.Harness.prototype._failList = function (fails) {
-    var last = -1;
-    var dash = '';
-    var list = [];
-    for (var i = 0; i < fails.length; i++) {
-        if (dash) {
-            // We're in a series of numbers.
-            if (fails[i] - 1 == last) {
-                // We're still in it.
-                last = fails[i];
-            } else {
-                // End of the line.
-                list[list.length-1] += dash + last;
-                last = -1;
-                list.push(fails[i]);
-                dash = '';
-            }
-        } else if (fails[i] - 1 == last) {
-            // We're in a new series.
-            last = fails[i];
-            dash = '-';
-        } else {
-            // Not in a sequence.
-            list.push(fails[i]);
-            last = fails[i];
-        }
-    }
-    if (dash) list[list.length-1] += dash + last;
-    return list.join(' ');
-}
@@ -1,37 +0,0 @@
-proto = new Subclass('Test.Jemplate', 'Test.Base');
-
-proto.init = function() {
-    Test.Base.prototype.init.call(this);
-    this.block_class = 'Test.Jemplate.Block';
-}
-
-proto = Subclass('Test.Jemplate.Block', 'Test.Base.Block');
-
-proto.init = function() {
-    Test.Base.Block.prototype.init.call(this);
-    this.filter_object = new Test.Jemplate.Filter();
-}
-
-proto = new Subclass('Test.Jemplate.Filter', 'Test.Base.Filter');
-
-proto.jemplate_process = function(content, block) {
-    var template;
-    if (content.match(/\n/))
-        template = content.split(/\n/)[0];
-    else
-        template = content;
-
-    var j = new Jemplate();
-    var data = block.data.context;
-    var result = Jemplate.process(template, data);
-    return result;
-}
-
-proto.raw_context = function( content, block ) {
-    try {
-        var context = eval("("+content+")");
-        block.data.context = context;
-    } catch(e) {
-        alert(e);
-    }
-}
@@ -1,18 +0,0 @@
-if (typeof dummy == 'undefined') {
-    dummy = function() {
-        this.context = arguments[0];
-        this.what = arguments[1];
-    };
-}
-
-dummy.prototype.simple = function() {
-    return 'This text came from the plugin';
-}
-
-dummy.prototype.params = function(one, two) {
-    return 'params: '+one+', '+two;
-}
-
-dummy.prototype.get_what = function() {
-    return 'what: ' + this.what;
-}
@@ -1,3408 +0,0 @@
-(function(){
-/*
- * jQuery 1.2.3 - New Wave Javascript
- *
- * Copyright (c) 2008 John Resig (jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
- * $Rev: 4663 $
- */
-
-// Map over jQuery in case of overwrite
-if ( window.jQuery )
-	var _jQuery = window.jQuery;
-
-var jQuery = window.jQuery = function( selector, context ) {
-	// The jQuery object is actually just the init constructor 'enhanced'
-	return new jQuery.prototype.init( selector, context );
-};
-
-// Map over the $ in case of overwrite
-if ( window.$ )
-	var _$ = window.$;
-	
-// Map the jQuery namespace to the '$' one
-window.$ = jQuery;
-
-// A simple way to check for HTML strings or ID strings
-// (both of which we optimize for)
-var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
-
-// Is it a simple selector
-var isSimple = /^.[^:#\[\.]*$/;
-
-jQuery.fn = jQuery.prototype = {
-	init: function( selector, context ) {
-		// Make sure that a selection was provided
-		selector = selector || document;
-
-		// Handle $(DOMElement)
-		if ( selector.nodeType ) {
-			this[0] = selector;
-			this.length = 1;
-			return this;
-
-		// Handle HTML strings
-		} else if ( typeof selector == "string" ) {
-			// Are we dealing with HTML string or an ID?
-			var match = quickExpr.exec( selector );
-
-			// Verify a match, and that no context was specified for #id
-			if ( match && (match[1] || !context) ) {
-
-				// HANDLE: $(html) -> $(array)
-				if ( match[1] )
-					selector = jQuery.clean( [ match[1] ], context );
-
-				// HANDLE: $("#id")
-				else {
-					var elem = document.getElementById( match[3] );
-
-					// Make sure an element was located
-					if ( elem )
-						// Handle the case where IE and Opera return items
-						// by name instead of ID
-						if ( elem.id != match[3] )
-							return jQuery().find( selector );
-
-						// Otherwise, we inject the element directly into the jQuery object
-						else {
-							this[0] = elem;
-							this.length = 1;
-							return this;
-						}
-
-					else
-						selector = [];
-				}
-
-			// HANDLE: $(expr, [context])
-			// (which is just equivalent to: $(content).find(expr)
-			} else
-				return new jQuery( context ).find( selector );
-
-		// HANDLE: $(function)
-		// Shortcut for document ready
-		} else if ( jQuery.isFunction( selector ) )
-			return new jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
-
-		return this.setArray(
-			// HANDLE: $(array)
-			selector.constructor == Array && selector ||
-
-			// HANDLE: $(arraylike)
-			// Watch for when an array-like object, contains DOM nodes, is passed in as the selector
-			(selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) ||
-
-			// HANDLE: $(*)
-			[ selector ] );
-	},
-	
-	// The current version of jQuery being used
-	jquery: "1.2.3",
-
-	// The number of elements contained in the matched element set
-	size: function() {
-		return this.length;
-	},
-	
-	// The number of elements contained in the matched element set
-	length: 0,
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-		return num == undefined ?
-
-			// Return a 'clean' array
-			jQuery.makeArray( this ) :
-
-			// Return just the object
-			this[ num ];
-	},
-	
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-		// Build a new jQuery matched element set
-		var ret = jQuery( elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-	
-	// Force the current matched set of elements to become
-	// the specified array of elements (destroying the stack in the process)
-	// You should use pushStack() in order to do this, but maintain the stack
-	setArray: function( elems ) {
-		// Resetting the length to 0, then using the native Array push
-		// is a super-fast way to populate an object with array-like properties
-		this.length = 0;
-		Array.prototype.push.apply( this, elems );
-		
-		return this;
-	},
-
-	// Execute a callback for every element in the matched set.
-	// (You can seed the arguments with an array of args, but this is
-	// only used internally.)
-	each: function( callback, args ) {
-		return jQuery.each( this, callback, args );
-	},
-
-	// Determine the position of an element within 
-	// the matched set of elements
-	index: function( elem ) {
-		var ret = -1;
-
-		// Locate the position of the desired element
-		this.each(function(i){
-			if ( this == elem )
-				ret = i;
-		});
-
-		return ret;
-	},
-
-	attr: function( name, value, type ) {
-		var options = name;
-		
-		// Look for the case where we're accessing a style value
-		if ( name.constructor == String )
-			if ( value == undefined )
-				return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined;
-
-			else {
-				options = {};
-				options[ name ] = value;
-			}
-		
-		// Check to see if we're setting style values
-		return this.each(function(i){
-			// Set all the styles
-			for ( name in options )
-				jQuery.attr(
-					type ?
-						this.style :
-						this,
-					name, jQuery.prop( this, options[ name ], type, i, name )
-				);
-		});
-	},
-
-	css: function( key, value ) {
-		// ignore negative width and height values
-		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
-			value = undefined;
-		return this.attr( key, value, "curCSS" );
-	},
-
-	text: function( text ) {
-		if ( typeof text != "object" && text != null )
-			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
-
-		var ret = "";
-
-		jQuery.each( text || this, function(){
-			jQuery.each( this.childNodes, function(){
-				if ( this.nodeType != 8 )
-					ret += this.nodeType != 1 ?
-						this.nodeValue :
-						jQuery.fn.text( [ this ] );
-			});
-		});
-
-		return ret;
-	},
-
-	wrapAll: function( html ) {
-		if ( this[0] )
-			// The elements to wrap the target around
-			jQuery( html, this[0].ownerDocument )
-				.clone()
-				.insertBefore( this[0] )
-				.map(function(){
-					var elem = this;
-
-					while ( elem.firstChild )
-						elem = elem.firstChild;
-
-					return elem;
-				})
-				.append(this);
-
-		return this;
-	},
-
-	wrapInner: function( html ) {
-		return this.each(function(){
-			jQuery( this ).contents().wrapAll( html );
-		});
-	},
-
-	wrap: function( html ) {
-		return this.each(function(){
-			jQuery( this ).wrapAll( html );
-		});
-	},
-
-	append: function() {
-		return this.domManip(arguments, true, false, function(elem){
-			if (this.nodeType == 1)
-				this.appendChild( elem );
-		});
-	},
-
-	prepend: function() {
-		return this.domManip(arguments, true, true, function(elem){
-			if (this.nodeType == 1)
-				this.insertBefore( elem, this.firstChild );
-		});
-	},
-	
-	before: function() {
-		return this.domManip(arguments, false, false, function(elem){
-			this.parentNode.insertBefore( elem, this );
-		});
-	},
-
-	after: function() {
-		return this.domManip(arguments, false, true, function(elem){
-			this.parentNode.insertBefore( elem, this.nextSibling );
-		});
-	},
-
-	end: function() {
-		return this.prevObject || jQuery( [] );
-	},
-
-	find: function( selector ) {
-		var elems = jQuery.map(this, function(elem){
-			return jQuery.find( selector, elem );
-		});
-
-		return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
-			jQuery.unique( elems ) :
-			elems );
-	},
-
-	clone: function( events ) {
-		// Do the clone
-		var ret = this.map(function(){
-			if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
-				// IE copies events bound via attachEvent when
-				// using cloneNode. Calling detachEvent on the
-				// clone will also remove the events from the orignal
-				// In order to get around this, we use innerHTML.
-				// Unfortunately, this means some modifications to 
-				// attributes in IE that are actually only stored 
-				// as properties will not be copied (such as the
-				// the name attribute on an input).
-				var clone = this.cloneNode(true),
-					container = document.createElement("div");
-				container.appendChild(clone);
-				return jQuery.clean([container.innerHTML])[0];
-			} else
-				return this.cloneNode(true);
-		});
-
-		// Need to set the expando to null on the cloned set if it exists
-		// removeData doesn't work here, IE removes it from the original as well
-		// this is primarily for IE but the data expando shouldn't be copied over in any browser
-		var clone = ret.find("*").andSelf().each(function(){
-			if ( this[ expando ] != undefined )
-				this[ expando ] = null;
-		});
-		
-		// Copy the events from the original to the clone
-		if ( events === true )
-			this.find("*").andSelf().each(function(i){
-				if (this.nodeType == 3)
-					return;
-				var events = jQuery.data( this, "events" );
-
-				for ( var type in events )
-					for ( var handler in events[ type ] )
-						jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
-			});
-
-		// Return the cloned set
-		return ret;
-	},
-
-	filter: function( selector ) {
-		return this.pushStack(
-			jQuery.isFunction( selector ) &&
-			jQuery.grep(this, function(elem, i){
-				return selector.call( elem, i );
-			}) ||
-
-			jQuery.multiFilter( selector, this ) );
-	},
-
-	not: function( selector ) {
-		if ( selector.constructor == String )
-			// test special case where just one selector is passed in
-			if ( isSimple.test( selector ) )
-				return this.pushStack( jQuery.multiFilter( selector, this, true ) );
-			else
-				selector = jQuery.multiFilter( selector, this );
-
-		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
-		return this.filter(function() {
-			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
-		});
-	},
-
-	add: function( selector ) {
-		return !selector ? this : this.pushStack( jQuery.merge( 
-			this.get(),
-			selector.constructor == String ? 
-				jQuery( selector ).get() :
-				selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ?
-					selector : [selector] ) );
-	},
-
-	is: function( selector ) {
-		return selector ?
-			jQuery.multiFilter( selector, this ).length > 0 :
-			false;
-	},
-
-	hasClass: function( selector ) {
-		return this.is( "." + selector );
-	},
-	
-	val: function( value ) {
-		if ( value == undefined ) {
-
-			if ( this.length ) {
-				var elem = this[0];
-
-				// We need to handle select boxes special
-				if ( jQuery.nodeName( elem, "select" ) ) {
-					var index = elem.selectedIndex,
-						values = [],
-						options = elem.options,
-						one = elem.type == "select-one";
-					
-					// Nothing was selected
-					if ( index < 0 )
-						return null;
-
-					// Loop through all the selected options
-					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
-						var option = options[ i ];
-
-						if ( option.selected ) {
-							// Get the specifc value for the option
-							value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
-							
-							// We don't need an array for one selects
-							if ( one )
-								return value;
-							
-							// Multi-Selects return an array
-							values.push( value );
-						}
-					}
-					
-					return values;
-					
-				// Everything else, we just grab the value
-				} else
-					return (this[0].value || "").replace(/\r/g, "");
-
-			}
-
-			return undefined;
-		}
-
-		return this.each(function(){
-			if ( this.nodeType != 1 )
-				return;
-
-			if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
-				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
-					jQuery.inArray(this.name, value) >= 0);
-
-			else if ( jQuery.nodeName( this, "select" ) ) {
-				var values = value.constructor == Array ?
-					value :
-					[ value ];
-
-				jQuery( "option", this ).each(function(){
-					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
-						jQuery.inArray( this.text, values ) >= 0);
-				});
-
-				if ( !values.length )
-					this.selectedIndex = -1;
-
-			} else
-				this.value = value;
-		});
-	},
-	
-	html: function( value ) {
-		return value == undefined ?
-			(this.length ?
-				this[0].innerHTML :
-				null) :
-			this.empty().append( value );
-	},
-
-	replaceWith: function( value ) {
-		return this.after( value ).remove();
-	},
-
-	eq: function( i ) {
-		return this.slice( i, i + 1 );
-	},
-
-	slice: function() {
-		return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map(this, function(elem, i){
-			return callback.call( elem, i, elem );
-		}));
-	},
-
-	andSelf: function() {
-		return this.add( this.prevObject );
-	},
-
-	data: function( key, value ){
-		var parts = key.split(".");
-		parts[1] = parts[1] ? "." + parts[1] : "";
-
-		if ( value == null ) {
-			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-			
-			if ( data == undefined && this.length )
-				data = jQuery.data( this[0], key );
-
-			return data == null && parts[1] ?
-				this.data( parts[0] ) :
-				data;
-		} else
-			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
-				jQuery.data( this, key, value );
-			});
-	},
-
-	removeData: function( key ){
-		return this.each(function(){
-			jQuery.removeData( this, key );
-		});
-	},
-	
-	domManip: function( args, table, reverse, callback ) {
-		var clone = this.length > 1, elems; 
-
-		return this.each(function(){
-			if ( !elems ) {
-				elems = jQuery.clean( args, this.ownerDocument );
-
-				if ( reverse )
-					elems.reverse();
-			}
-
-			var obj = this;
-
-			if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
-				obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
-
-			var scripts = jQuery( [] );
-
-			jQuery.each(elems, function(){
-				var elem = clone ?
-					jQuery( this ).clone( true )[0] :
-					this;
-
-				// execute all scripts after the elements have been injected
-				if ( jQuery.nodeName( elem, "script" ) ) {
-					scripts = scripts.add( elem );
-				} else {
-					// Remove any inner scripts for later evaluation
-					if ( elem.nodeType == 1 )
-						scripts = scripts.add( jQuery( "script", elem ).remove() );
-
-					// Inject the elements into the document
-					callback.call( obj, elem );
-				}
-			});
-
-			scripts.each( evalScript );
-		});
-	}
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.prototype.init.prototype = jQuery.prototype;
-
-function evalScript( i, elem ) {
-	if ( elem.src )
-		jQuery.ajax({
-			url: elem.src,
-			async: false,
-			dataType: "script"
-		});
-
-	else
-		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
-
-	if ( elem.parentNode )
-		elem.parentNode.removeChild( elem );
-}
-
-jQuery.extend = jQuery.fn.extend = function() {
-	// copy reference to target object
-	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
-
-	// Handle a deep copy situation
-	if ( target.constructor == Boolean ) {
-		deep = target;
-		target = arguments[1] || {};
-		// skip the boolean and the target
-		i = 2;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target != "object" && typeof target != "function" )
-		target = {};
-
-	// extend jQuery itself if only one argument is passed
-	if ( length == 1 ) {
-		target = this;
-		i = 0;
-	}
-
-	for ( ; i < length; i++ )
-		// Only deal with non-null/undefined values
-		if ( (options = arguments[ i ]) != null )
-			// Extend the base object
-			for ( var name in options ) {
-				// Prevent never-ending loop
-				if ( target === options[ name ] )
-					continue;
-
-				// Recurse if we're merging object values
-				if ( deep && options[ name ] && typeof options[ name ] == "object" && target[ name ] && !options[ name ].nodeType )
-					target[ name ] = jQuery.extend( target[ name ], options[ name ] );
-
-				// Don't bring in undefined values
-				else if ( options[ name ] != undefined )
-					target[ name ] = options[ name ];
-
-			}
-
-	// Return the modified object
-	return target;
-};
-
-var expando = "jQuery" + (new Date()).getTime(), uuid = 0, windowData = {};
-
-// exclude the following css properties to add px
-var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;
-
-jQuery.extend({
-	noConflict: function( deep ) {
-		window.$ = _$;
-
-		if ( deep )
-			window.jQuery = _jQuery;
-
-		return jQuery;
-	},
-
-	// See test/unit/core.js for details concerning this function.
-	isFunction: function( fn ) {
-		return !!fn && typeof fn != "string" && !fn.nodeName && 
-			fn.constructor != Array && /function/i.test( fn + "" );
-	},
-	
-	// check if an element is in a (or is an) XML document
-	isXMLDoc: function( elem ) {
-		return elem.documentElement && !elem.body ||
-			elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
-	},
-
-	// Evalulates a script in a global context
-	globalEval: function( data ) {
-		data = jQuery.trim( data );
-
-		if ( data ) {
-			// Inspired by code by Andrea Giammarchi
-			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
-			var head = document.getElementsByTagName("head")[0] || document.documentElement,
-				script = document.createElement("script");
-
-			script.type = "text/javascript";
-			if ( jQuery.browser.msie )
-				script.text = data;
-			else
-				script.appendChild( document.createTextNode( data ) );
-
-			head.appendChild( script );
-			head.removeChild( script );
-		}
-	},
-
-	nodeName: function( elem, name ) {
-		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
-	},
-	
-	cache: {},
-	
-	data: function( elem, name, data ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// Compute a unique ID for the element
-		if ( !id ) 
-			id = elem[ expando ] = ++uuid;
-
-		// Only generate the data cache if we're
-		// trying to access or manipulate it
-		if ( name && !jQuery.cache[ id ] )
-			jQuery.cache[ id ] = {};
-		
-		// Prevent overriding the named cache with undefined values
-		if ( data != undefined )
-			jQuery.cache[ id ][ name ] = data;
-		
-		// Return the named cache data, or the ID for the element	
-		return name ?
-			jQuery.cache[ id ][ name ] :
-			id;
-	},
-	
-	removeData: function( elem, name ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// If we want to remove a specific section of the element's data
-		if ( name ) {
-			if ( jQuery.cache[ id ] ) {
-				// Remove the section of cache data
-				delete jQuery.cache[ id ][ name ];
-
-				// If we've removed all the data, remove the element's cache
-				name = "";
-
-				for ( name in jQuery.cache[ id ] )
-					break;
-
-				if ( !name )
-					jQuery.removeData( elem );
-			}
-
-		// Otherwise, we want to remove all of the element's data
-		} else {
-			// Clean up the element expando
-			try {
-				delete elem[ expando ];
-			} catch(e){
-				// IE has trouble directly removing the expando
-				// but it's ok with using removeAttribute
-				if ( elem.removeAttribute )
-					elem.removeAttribute( expando );
-			}
-
-			// Completely remove the data cache
-			delete jQuery.cache[ id ];
-		}
-	},
-
-	// args is for internal usage only
-	each: function( object, callback, args ) {
-		if ( args ) {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.apply( object[ name ], args ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length; i < length; i++ )
-					if ( callback.apply( object[ i ], args ) === false )
-						break;
-
-		// A special, fast, case for the most common use of each
-		} else {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.call( object[ name ], name, object[ name ] ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length, value = object[0]; 
-					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
-		}
-
-		return object;
-	},
-	
-	prop: function( elem, value, type, i, name ) {
-			// Handle executable functions
-			if ( jQuery.isFunction( value ) )
-				value = value.call( elem, i );
-				
-			// Handle passing in a number to a CSS property
-			return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
-				value + "px" :
-				value;
-	},
-
-	className: {
-		// internal only, use addClass("class")
-		add: function( elem, classNames ) {
-			jQuery.each((classNames || "").split(/\s+/), function(i, className){
-				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
-					elem.className += (elem.className ? " " : "") + className;
-			});
-		},
-
-		// internal only, use removeClass("class")
-		remove: function( elem, classNames ) {
-			if (elem.nodeType == 1)
-				elem.className = classNames != undefined ?
-					jQuery.grep(elem.className.split(/\s+/), function(className){
-						return !jQuery.className.has( classNames, className );	
-					}).join(" ") :
-					"";
-		},
-
-		// internal only, use is(".class")
-		has: function( elem, className ) {
-			return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
-		}
-	},
-
-	// A method for quickly swapping in/out CSS properties to get correct calculations
-	swap: function( elem, options, callback ) {
-		var old = {};
-		// Remember the old values, and insert the new ones
-		for ( var name in options ) {
-			old[ name ] = elem.style[ name ];
-			elem.style[ name ] = options[ name ];
-		}
-
-		callback.call( elem );
-
-		// Revert the old values
-		for ( var name in options )
-			elem.style[ name ] = old[ name ];
-	},
-
-	css: function( elem, name, force ) {
-		if ( name == "width" || name == "height" ) {
-			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
-		
-			function getWH() {
-				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
-				var padding = 0, border = 0;
-				jQuery.each( which, function() {
-					padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
-					border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
-				});
-				val -= Math.round(padding + border);
-			}
-		
-			if ( jQuery(elem).is(":visible") )
-				getWH();
-			else
-				jQuery.swap( elem, props, getWH );
-			
-			return Math.max(0, val);
-		}
-		
-		return jQuery.curCSS( elem, name, force );
-	},
-
-	curCSS: function( elem, name, force ) {
-		var ret;
-
-		// A helper method for determining if an element's values are broken
-		function color( elem ) {
-			if ( !jQuery.browser.safari )
-				return false;
-
-			var ret = document.defaultView.getComputedStyle( elem, null );
-			return !ret || ret.getPropertyValue("color") == "";
-		}
-
-		// We need to handle opacity special in IE
-		if ( name == "opacity" && jQuery.browser.msie ) {
-			ret = jQuery.attr( elem.style, "opacity" );
-
-			return ret == "" ?
-				"1" :
-				ret;
-		}
-		// Opera sometimes will give the wrong display answer, this fixes it, see #2037
-		if ( jQuery.browser.opera && name == "display" ) {
-			var save = elem.style.outline;
-			elem.style.outline = "0 solid black";
-			elem.style.outline = save;
-		}
-		
-		// Make sure we're using the right name for getting the float value
-		if ( name.match( /float/i ) )
-			name = styleFloat;
-
-		if ( !force && elem.style && elem.style[ name ] )
-			ret = elem.style[ name ];
-
-		else if ( document.defaultView && document.defaultView.getComputedStyle ) {
-
-			// Only "float" is needed here
-			if ( name.match( /float/i ) )
-				name = "float";
-
-			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
-
-			var getComputedStyle = document.defaultView.getComputedStyle( elem, null );
-
-			if ( getComputedStyle && !color( elem ) )
-				ret = getComputedStyle.getPropertyValue( name );
-
-			// If the element isn't reporting its values properly in Safari
-			// then some display: none elements are involved
-			else {
-				var swap = [], stack = [];
-
-				// Locate all of the parent display: none elements
-				for ( var a = elem; a && color(a); a = a.parentNode )
-					stack.unshift(a);
-
-				// Go through and make them visible, but in reverse
-				// (It would be better if we knew the exact display type that they had)
-				for ( var i = 0; i < stack.length; i++ )
-					if ( color( stack[ i ] ) ) {
-						swap[ i ] = stack[ i ].style.display;
-						stack[ i ].style.display = "block";
-					}
-
-				// Since we flip the display style, we have to handle that
-				// one special, otherwise get the value
-				ret = name == "display" && swap[ stack.length - 1 ] != null ?
-					"none" :
-					( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || "";
-
-				// Finally, revert the display styles back
-				for ( var i = 0; i < swap.length; i++ )
-					if ( swap[ i ] != null )
-						stack[ i ].style.display = swap[ i ];
-			}
-
-			// We should always get a number back from opacity
-			if ( name == "opacity" && ret == "" )
-				ret = "1";
-
-		} else if ( elem.currentStyle ) {
-			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
-
-			// From the awesome hack by Dean Edwards
-			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
-
-			// If we're not dealing with a regular pixel number
-			// but a number that has a weird ending, we need to convert it to pixels
-			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
-				// Remember the original values
-				var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left;
-
-				// Put in the new values to get a computed value out
-				elem.runtimeStyle.left = elem.currentStyle.left;
-				elem.style.left = ret || 0;
-				ret = elem.style.pixelLeft + "px";
-
-				// Revert the changed values
-				elem.style.left = style;
-				elem.runtimeStyle.left = runtimeStyle;
-			}
-		}
-
-		return ret;
-	},
-	
-	clean: function( elems, context ) {
-		var ret = [];
-		context = context || document;
-		// !context.createElement fails in IE with an error but returns typeof 'object'
-		if (typeof context.createElement == 'undefined') 
-			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
-
-		jQuery.each(elems, function(i, elem){
-			if ( !elem )
-				return;
-
-			if ( elem.constructor == Number )
-				elem = elem.toString();
-			
-			// Convert html string into DOM nodes
-			if ( typeof elem == "string" ) {
-				// Fix "XHTML"-style tags in all browsers
-				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
-					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
-						all :
-						front + "></" + tag + ">";
-				});
-
-				// Trim whitespace, otherwise indexOf won't work as expected
-				var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
-
-				var wrap =
-					// option or optgroup
-					!tags.indexOf("<opt") &&
-					[ 1, "<select multiple='multiple'>", "</select>" ] ||
-					
-					!tags.indexOf("<leg") &&
-					[ 1, "<fieldset>", "</fieldset>" ] ||
-					
-					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
-					[ 1, "<table>", "</table>" ] ||
-					
-					!tags.indexOf("<tr") &&
-					[ 2, "<table><tbody>", "</tbody></table>" ] ||
-					
-				 	// <thead> matched above
-					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
-					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
-					
-					!tags.indexOf("<col") &&
-					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
-
-					// IE can't serialize <link> and <script> tags normally
-					jQuery.browser.msie &&
-					[ 1, "div<div>", "</div>" ] ||
-					
-					[ 0, "", "" ];
-
-				// Go to html and back, then peel off extra wrappers
-				div.innerHTML = wrap[1] + elem + wrap[2];
-				
-				// Move to the right depth
-				while ( wrap[0]-- )
-					div = div.lastChild;
-				
-				// Remove IE's autoinserted <tbody> from table fragments
-				if ( jQuery.browser.msie ) {
-					
-					// String was a <table>, *may* have spurious <tbody>
-					var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
-						div.firstChild && div.firstChild.childNodes :
-						
-						// String was a bare <thead> or <tfoot>
-						wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
-							div.childNodes :
-							[];
-				
-					for ( var j = tbody.length - 1; j >= 0 ; --j )
-						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
-							tbody[ j ].parentNode.removeChild( tbody[ j ] );
-					
-					// IE completely kills leading whitespace when innerHTML is used	
-					if ( /^\s/.test( elem ) )	
-						div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
-				
-				}
-				
-				elem = jQuery.makeArray( div.childNodes );
-			}
-
-			if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
-				return;
-
-			if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
-				ret.push( elem );
-
-			else
-				ret = jQuery.merge( ret, elem );
-
-		});
-
-		return ret;
-	},
-	
-	attr: function( elem, name, value ) {
-		// don't set attributes on text and comment nodes
-		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
-			return undefined;
-
-		var fix = jQuery.isXMLDoc( elem ) ?
-			{} :
-			jQuery.props;
-
-		// Safari mis-reports the default selected property of a hidden option
-		// Accessing the parent's selectedIndex property fixes it
-		if ( name == "selected" && jQuery.browser.safari )
-			elem.parentNode.selectedIndex;
-		
-		// Certain attributes only work when accessed via the old DOM 0 way
-		if ( fix[ name ] ) {
-			if ( value != undefined )
-				elem[ fix[ name ] ] = value;
-
-			return elem[ fix[ name ] ];
-
-		} else if ( jQuery.browser.msie && name == "style" )
-			return jQuery.attr( elem.style, "cssText", value );
-
-		else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName( elem, "form" ) && (name == "action" || name == "method") )
-			return elem.getAttributeNode( name ).nodeValue;
-
-		// IE elem.getAttribute passes even for style
-		else if ( elem.tagName ) {
-
-			if ( value != undefined ) {
-				// We can't allow the type property to be changed (since it causes problems in IE)
-				if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
-					throw "type property can't be changed";
-
-				// convert the value to a string (all browsers do this but IE) see #1070
-				elem.setAttribute( name, "" + value );
-			}
-
-			if ( jQuery.browser.msie && /href|src/.test( name ) && !jQuery.isXMLDoc( elem ) ) 
-				return elem.getAttribute( name, 2 );
-
-			return elem.getAttribute( name );
-
-		// elem is actually elem.style ... set the style
-		} else {
-			// IE actually uses filters for opacity
-			if ( name == "opacity" && jQuery.browser.msie ) {
-				if ( value != undefined ) {
-					// IE has trouble with opacity if it does not have layout
-					// Force it by setting the zoom level
-					elem.zoom = 1; 
-	
-					// Set the alpha filter to set the opacity
-					elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
-						(parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
-				}
-	
-				return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
-					(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() :
-					"";
-			}
-
-			name = name.replace(/-([a-z])/ig, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			if ( value != undefined )
-				elem[ name ] = value;
-
-			return elem[ name ];
-		}
-	},
-	
-	trim: function( text ) {
-		return (text || "").replace( /^\s+|\s+$/g, "" );
-	},
-
-	makeArray: function( array ) {
-		var ret = [];
-
-		// Need to use typeof to fight Safari childNodes crashes
-		if ( typeof array != "array" )
-			for ( var i = 0, length = array.length; i < length; i++ )
-				ret.push( array[ i ] );
-		else
-			ret = array.slice( 0 );
-
-		return ret;
-	},
-
-	inArray: function( elem, array ) {
-		for ( var i = 0, length = array.length; i < length; i++ )
-			if ( array[ i ] == elem )
-				return i;
-
-		return -1;
-	},
-
-	merge: function( first, second ) {
-		// We have to loop this way because IE & Opera overwrite the length
-		// expando of getElementsByTagName
-
-		// Also, we need to make sure that the correct elements are being returned
-		// (IE returns comment nodes in a '*' query)
-		if ( jQuery.browser.msie ) {
-			for ( var i = 0; second[ i ]; i++ )
-				if ( second[ i ].nodeType != 8 )
-					first.push( second[ i ] );
-
-		} else
-			for ( var i = 0; second[ i ]; i++ )
-				first.push( second[ i ] );
-
-		return first;
-	},
-
-	unique: function( array ) {
-		var ret = [], done = {};
-
-		try {
-
-			for ( var i = 0, length = array.length; i < length; i++ ) {
-				var id = jQuery.data( array[ i ] );
-
-				if ( !done[ id ] ) {
-					done[ id ] = true;
-					ret.push( array[ i ] );
-				}
-			}
-
-		} catch( e ) {
-			ret = array;
-		}
-
-		return ret;
-	},
-
-	grep: function( elems, callback, inv ) {
-		var ret = [];
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( var i = 0, length = elems.length; i < length; i++ )
-			if ( !inv && callback( elems[ i ], i ) || inv && !callback( elems[ i ], i ) )
-				ret.push( elems[ i ] );
-
-		return ret;
-	},
-
-	map: function( elems, callback ) {
-		var ret = [];
-
-		// Go through the array, translating each of the items to their
-		// new value (or values).
-		for ( var i = 0, length = elems.length; i < length; i++ ) {
-			var value = callback( elems[ i ], i );
-
-			if ( value !== null && value != undefined ) {
-				if ( value.constructor != Array )
-					value = [ value ];
-
-				ret = ret.concat( value );
-			}
-		}
-
-		return ret;
-	}
-});
-
-var userAgent = navigator.userAgent.toLowerCase();
-
-// Figure out what browser is being used
-jQuery.browser = {
-	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
-	safari: /webkit/.test( userAgent ),
-	opera: /opera/.test( userAgent ),
-	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
-	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
-};
-
-var styleFloat = jQuery.browser.msie ?
-	"styleFloat" :
-	"cssFloat";
-	
-jQuery.extend({
-	// Check to see if the W3C box model is being used
-	boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
-	
-	props: {
-		"for": "htmlFor",
-		"class": "className",
-		"float": styleFloat,
-		cssFloat: styleFloat,
-		styleFloat: styleFloat,
-		innerHTML: "innerHTML",
-		className: "className",
-		value: "value",
-		disabled: "disabled",
-		checked: "checked",
-		readonly: "readOnly",
-		selected: "selected",
-		maxlength: "maxLength",
-		selectedIndex: "selectedIndex",
-		defaultValue: "defaultValue",
-		tagName: "tagName",
-		nodeName: "nodeName"
-	}
-});
-
-jQuery.each({
-	parent: function(elem){return elem.parentNode;},
-	parents: function(elem){return jQuery.dir(elem,"parentNode");},
-	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
-	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
-	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
-	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
-	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
-	children: function(elem){return jQuery.sibling(elem.firstChild);},
-	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
-}, function(name, fn){
-	jQuery.fn[ name ] = function( selector ) {
-		var ret = jQuery.map( this, fn );
-
-		if ( selector && typeof selector == "string" )
-			ret = jQuery.multiFilter( selector, ret );
-
-		return this.pushStack( jQuery.unique( ret ) );
-	};
-});
-
-jQuery.each({
-	appendTo: "append",
-	prependTo: "prepend",
-	insertBefore: "before",
-	insertAfter: "after",
-	replaceAll: "replaceWith"
-}, function(name, original){
-	jQuery.fn[ name ] = function() {
-		var args = arguments;
-
-		return this.each(function(){
-			for ( var i = 0, length = args.length; i < length; i++ )
-				jQuery( args[ i ] )[ original ]( this );
-		});
-	};
-});
-
-jQuery.each({
-	removeAttr: function( name ) {
-		jQuery.attr( this, name, "" );
-		if (this.nodeType == 1) 
-			this.removeAttribute( name );
-	},
-
-	addClass: function( classNames ) {
-		jQuery.className.add( this, classNames );
-	},
-
-	removeClass: function( classNames ) {
-		jQuery.className.remove( this, classNames );
-	},
-
-	toggleClass: function( classNames ) {
-		jQuery.className[ jQuery.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
-	},
-
-	remove: function( selector ) {
-		if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
-			// Prevent memory leaks
-			jQuery( "*", this ).add(this).each(function(){
-				jQuery.event.remove(this);
-				jQuery.removeData(this);
-			});
-			if (this.parentNode)
-				this.parentNode.removeChild( this );
-		}
-	},
-
-	empty: function() {
-		// Remove element nodes and prevent memory leaks
-		jQuery( ">*", this ).remove();
-		
-		// Remove any remaining nodes
-		while ( this.firstChild )
-			this.removeChild( this.firstChild );
-	}
-}, function(name, fn){
-	jQuery.fn[ name ] = function(){
-		return this.each( fn, arguments );
-	};
-});
-
-jQuery.each([ "Height", "Width" ], function(i, name){
-	var type = name.toLowerCase();
-	
-	jQuery.fn[ type ] = function( size ) {
-		// Get window width or height
-		return this[0] == window ?
-			// Opera reports document.body.client[Width/Height] properly in both quirks and standards
-			jQuery.browser.opera && document.body[ "client" + name ] || 
-			
-			// Safari reports inner[Width/Height] just fine (Mozilla and Opera include scroll bar widths)
-			jQuery.browser.safari && window[ "inner" + name ] ||
-			
-			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
-			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || document.body[ "client" + name ] :
-		
-			// Get document width or height
-			this[0] == document ?
-				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
-				Math.max( 
-					Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), 
-					Math.max(document.body["offset" + name], document.documentElement["offset" + name]) 
-				) :
-
-				// Get or set width or height on the element
-				size == undefined ?
-					// Get width or height on the element
-					(this.length ? jQuery.css( this[0], type ) : null) :
-
-					// Set the width or height on the element (default to pixels if value is unitless)
-					this.css( type, size.constructor == String ? size : size + "px" );
-	};
-});
-
-var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417 ?
-		"(?:[\\w*_-]|\\\\.)" :
-		"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
-	quickChild = new RegExp("^>\\s*(" + chars + "+)"),
-	quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),
-	quickClass = new RegExp("^([#.]?)(" + chars + "*)");
-
-jQuery.extend({
-	expr: {
-		"": function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},
-		"#": function(a,i,m){return a.getAttribute("id")==m[2];},
-		":": {
-			// Position Checks
-			lt: function(a,i,m){return i<m[3]-0;},
-			gt: function(a,i,m){return i>m[3]-0;},
-			nth: function(a,i,m){return m[3]-0==i;},
-			eq: function(a,i,m){return m[3]-0==i;},
-			first: function(a,i){return i==0;},
-			last: function(a,i,m,r){return i==r.length-1;},
-			even: function(a,i){return i%2==0;},
-			odd: function(a,i){return i%2;},
-
-			// Child Checks
-			"first-child": function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},
-			"last-child": function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},
-			"only-child": function(a){return !jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},
-
-			// Parent Checks
-			parent: function(a){return a.firstChild;},
-			empty: function(a){return !a.firstChild;},
-
-			// Text Check
-			contains: function(a,i,m){return (a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},
-
-			// Visibility
-			visible: function(a){return "hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},
-			hidden: function(a){return "hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},
-
-			// Form attributes
-			enabled: function(a){return !a.disabled;},
-			disabled: function(a){return a.disabled;},
-			checked: function(a){return a.checked;},
-			selected: function(a){return a.selected||jQuery.attr(a,"selected");},
-
-			// Form elements
-			text: function(a){return "text"==a.type;},
-			radio: function(a){return "radio"==a.type;},
-			checkbox: function(a){return "checkbox"==a.type;},
-			file: function(a){return "file"==a.type;},
-			password: function(a){return "password"==a.type;},
-			submit: function(a){return "submit"==a.type;},
-			image: function(a){return "image"==a.type;},
-			reset: function(a){return "reset"==a.type;},
-			button: function(a){return "button"==a.type||jQuery.nodeName(a,"button");},
-			input: function(a){return /input|select|textarea|button/i.test(a.nodeName);},
-
-			// :has()
-			has: function(a,i,m){return jQuery.find(m[3],a).length;},
-
-			// :header
-			header: function(a){return /h\d/i.test(a.nodeName);},
-
-			// :animated
-			animated: function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}
-		}
-	},
-	
-	// The regular expressions that power the parsing engine
-	parse: [
-		// Match: [@value='test'], [@foo]
-		/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
-
-		// Match: :contains('foo')
-		/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
-
-		// Match: :even, :last-chlid, #id, .class
-		new RegExp("^([:.#]*)(" + chars + "+)")
-	],
-
-	multiFilter: function( expr, elems, not ) {
-		var old, cur = [];
-
-		while ( expr && expr != old ) {
-			old = expr;
-			var f = jQuery.filter( expr, elems, not );
-			expr = f.t.replace(/^\s*,\s*/, "" );
-			cur = not ? elems = f.r : jQuery.merge( cur, f.r );
-		}
-
-		return cur;
-	},
-
-	find: function( t, context ) {
-		// Quickly handle non-string expressions
-		if ( typeof t != "string" )
-			return [ t ];
-
-		// check to make sure context is a DOM element or a document
-		if ( context && context.nodeType != 1 && context.nodeType != 9)
-			return [ ];
-
-		// Set the correct context (if none is provided)
-		context = context || document;
-
-		// Initialize the search
-		var ret = [context], done = [], last, nodeName;
-
-		// Continue while a selector expression exists, and while
-		// we're no longer looping upon ourselves
-		while ( t && last != t ) {
-			var r = [];
-			last = t;
-
-			t = jQuery.trim(t);
-
-			var foundToken = false;
-
-			// An attempt at speeding up child selectors that
-			// point to a specific element tag
-			var re = quickChild;
-			var m = re.exec(t);
-
-			if ( m ) {
-				nodeName = m[1].toUpperCase();
-
-				// Perform our own iteration and filter
-				for ( var i = 0; ret[i]; i++ )
-					for ( var c = ret[i].firstChild; c; c = c.nextSibling )
-						if ( c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName) )
-							r.push( c );
-
-				ret = r;
-				t = t.replace( re, "" );
-				if ( t.indexOf(" ") == 0 ) continue;
-				foundToken = true;
-			} else {
-				re = /^([>+~])\s*(\w*)/i;
-
-				if ( (m = re.exec(t)) != null ) {
-					r = [];
-
-					var merge = {};
-					nodeName = m[2].toUpperCase();
-					m = m[1];
-
-					for ( var j = 0, rl = ret.length; j < rl; j++ ) {
-						var n = m == "~" || m == "+" ? ret[j].nextSibling : ret[j].firstChild;
-						for ( ; n; n = n.nextSibling )
-							if ( n.nodeType == 1 ) {
-								var id = jQuery.data(n);
-
-								if ( m == "~" && merge[id] ) break;
-								
-								if (!nodeName || n.nodeName.toUpperCase() == nodeName ) {
-									if ( m == "~" ) merge[id] = true;
-									r.push( n );
-								}
-								
-								if ( m == "+" ) break;
-							}
-					}
-
-					ret = r;
-
-					// And remove the token
-					t = jQuery.trim( t.replace( re, "" ) );
-					foundToken = true;
-				}
-			}
-
-			// See if there's still an expression, and that we haven't already
-			// matched a token
-			if ( t && !foundToken ) {
-				// Handle multiple expressions
-				if ( !t.indexOf(",") ) {
-					// Clean the result set
-					if ( context == ret[0] ) ret.shift();
-
-					// Merge the result sets
-					done = jQuery.merge( done, ret );
-
-					// Reset the context
-					r = ret = [context];
-
-					// Touch up the selector string
-					t = " " + t.substr(1,t.length);
-
-				} else {
-					// Optimize for the case nodeName#idName
-					var re2 = quickID;
-					var m = re2.exec(t);
-					
-					// Re-organize the results, so that they're consistent
-					if ( m ) {
-						m = [ 0, m[2], m[3], m[1] ];
-
-					} else {
-						// Otherwise, do a traditional filter check for
-						// ID, class, and element selectors
-						re2 = quickClass;
-						m = re2.exec(t);
-					}
-
-					m[2] = m[2].replace(/\\/g, "");
-
-					var elem = ret[ret.length-1];
-
-					// Try to do a global search by ID, where we can
-					if ( m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem) ) {
-						// Optimization for HTML document case
-						var oid = elem.getElementById(m[2]);
-						
-						// Do a quick check for the existence of the actual ID attribute
-						// to avoid selecting by the name attribute in IE
-						// also check to insure id is a string to avoid selecting an element with the name of 'id' inside a form
-						if ( (jQuery.browser.msie||jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2] )
-							oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
-
-						// Do a quick check for node name (where applicable) so
-						// that div#foo searches will be really fast
-						ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
-					} else {
-						// We need to find all descendant elements
-						for ( var i = 0; ret[i]; i++ ) {
-							// Grab the tag name being searched for
-							var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*" : m[2];
-
-							// Handle IE7 being really dumb about <object>s
-							if ( tag == "*" && ret[i].nodeName.toLowerCase() == "object" )
-								tag = "param";
-
-							r = jQuery.merge( r, ret[i].getElementsByTagName( tag ));
-						}
-
-						// It's faster to filter by class and be done with it
-						if ( m[1] == "." )
-							r = jQuery.classFilter( r, m[2] );
-
-						// Same with ID filtering
-						if ( m[1] == "#" ) {
-							var tmp = [];
-
-							// Try to find the element with the ID
-							for ( var i = 0; r[i]; i++ )
-								if ( r[i].getAttribute("id") == m[2] ) {
-									tmp = [ r[i] ];
-									break;
-								}
-
-							r = tmp;
-						}
-
-						ret = r;
-					}
-
-					t = t.replace( re2, "" );
-				}
-
-			}
-
-			// If a selector string still exists
-			if ( t ) {
-				// Attempt to filter it
-				var val = jQuery.filter(t,r);
-				ret = r = val.r;
-				t = jQuery.trim(val.t);
-			}
-		}
-
-		// An error occurred with the selector;
-		// just return an empty set instead
-		if ( t )
-			ret = [];
-
-		// Remove the root context
-		if ( ret && context == ret[0] )
-			ret.shift();
-
-		// And combine the results
-		done = jQuery.merge( done, ret );
-
-		return done;
-	},
-
-	classFilter: function(r,m,not){
-		m = " " + m + " ";
-		var tmp = [];
-		for ( var i = 0; r[i]; i++ ) {
-			var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
-			if ( !not && pass || not && !pass )
-				tmp.push( r[i] );
-		}
-		return tmp;
-	},
-
-	filter: function(t,r,not) {
-		var last;
-
-		// Look for common filter expressions
-		while ( t && t != last ) {
-			last = t;
-
-			var p = jQuery.parse, m;
-
-			for ( var i = 0; p[i]; i++ ) {
-				m = p[i].exec( t );
-
-				if ( m ) {
-					// Remove what we just matched
-					t = t.substring( m[0].length );
-
-					m[2] = m[2].replace(/\\/g, "");
-					break;
-				}
-			}
-
-			if ( !m )
-				break;
-
-			// :not() is a special case that can be optimized by
-			// keeping it out of the expression list
-			if ( m[1] == ":" && m[2] == "not" )
-				// optimize if only one selector found (most common case)
-				r = isSimple.test( m[3] ) ?
-					jQuery.filter(m[3], r, true).r :
-					jQuery( r ).not( m[3] );
-
-			// We can get a big speed boost by filtering by class here
-			else if ( m[1] == "." )
-				r = jQuery.classFilter(r, m[2], not);
-
-			else if ( m[1] == "[" ) {
-				var tmp = [], type = m[3];
-				
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var a = r[i], z = a[ jQuery.props[m[2]] || m[2] ];
-					
-					if ( z == null || /href|src|selected/.test(m[2]) )
-						z = jQuery.attr(a,m[2]) || '';
-
-					if ( (type == "" && !!z ||
-						 type == "=" && z == m[5] ||
-						 type == "!=" && z != m[5] ||
-						 type == "^=" && z && !z.indexOf(m[5]) ||
-						 type == "$=" && z.substr(z.length - m[5].length) == m[5] ||
-						 (type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0) ^ not )
-							tmp.push( a );
-				}
-				
-				r = tmp;
-
-			// We can get a speed boost by handling nth-child here
-			} else if ( m[1] == ":" && m[2] == "nth-child" ) {
-				var merge = {}, tmp = [],
-					// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
-					test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
-						m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||
-						!/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
-					// calculate the numbers (first)n+(last) including if they are negative
-					first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
- 
-				// loop through all the elements left in the jQuery object
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
-
-					if ( !merge[id] ) {
-						var c = 1;
-
-						for ( var n = parentNode.firstChild; n; n = n.nextSibling )
-							if ( n.nodeType == 1 )
-								n.nodeIndex = c++;
-
-						merge[id] = true;
-					}
-
-					var add = false;
-
-					if ( first == 0 ) {
-						if ( node.nodeIndex == last )
-							add = true;
-					} else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
-						add = true;
-
-					if ( add ^ not )
-						tmp.push( node );
-				}
-
-				r = tmp;
-
-			// Otherwise, find the expression to execute
-			} else {
-				var fn = jQuery.expr[ m[1] ];
-				if ( typeof fn == "object" )
-					fn = fn[ m[2] ];
-
-				if ( typeof fn == "string" )
-					fn = eval("false||function(a,i){return " + fn + ";}");
-
-				// Execute it against the current filter
-				r = jQuery.grep( r, function(elem, i){
-					return fn(elem, i, m, r);
-				}, not );
-			}
-		}
-
-		// Return an array of filtered elements (r)
-		// and the modified expression string (t)
-		return { r: r, t: t };
-	},
-
-	dir: function( elem, dir ){
-		var matched = [];
-		var cur = elem[dir];
-		while ( cur && cur != document ) {
-			if ( cur.nodeType == 1 )
-				matched.push( cur );
-			cur = cur[dir];
-		}
-		return matched;
-	},
-	
-	nth: function(cur,result,dir,elem){
-		result = result || 1;
-		var num = 0;
-
-		for ( ; cur; cur = cur[dir] )
-			if ( cur.nodeType == 1 && ++num == result )
-				break;
-
-		return cur;
-	},
-	
-	sibling: function( n, elem ) {
-		var r = [];
-
-		for ( ; n; n = n.nextSibling ) {
-			if ( n.nodeType == 1 && (!elem || n != elem) )
-				r.push( n );
-		}
-
-		return r;
-	}
-});
-
-/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code orignated from 
- * Dean Edwards' addEvent library.
- */
-jQuery.event = {
-
-	// Bind an event to an element
-	// Original by Dean Edwards
-	add: function(elem, types, handler, data) {
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		// For whatever reason, IE has trouble passing the window object
-		// around, causing it to be cloned in the process
-		if ( jQuery.browser.msie && elem.setInterval != undefined )
-			elem = window;
-
-		// Make sure that the function being executed has a unique ID
-		if ( !handler.guid )
-			handler.guid = this.guid++;
-			
-		// if data is passed, bind to handler 
-		if( data != undefined ) { 
-			// Create temporary function pointer to original handler 
-			var fn = handler; 
-
-			// Create unique handler function, wrapped around original handler 
-			handler = function() { 
-				// Pass arguments and context to original handler 
-				return fn.apply(this, arguments); 
-			};
-
-			// Store data in unique handler 
-			handler.data = data;
-
-			// Set the guid of unique handler to the same of original handler, so it can be removed 
-			handler.guid = fn.guid;
-		}
-
-		// Init the element's event structure
-		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
-			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
-				// returned undefined or false
-				var val;
-
-				// Handle the second event of a trigger and when
-				// an event is called after a page has unloaded
-				if ( typeof jQuery == "undefined" || jQuery.event.triggered )
-					return val;
-		
-				val = jQuery.event.handle.apply(arguments.callee.elem, arguments);
-		
-				return val;
-			});
-		// Add elem as a property of the handle function
-		// This is to prevent a memory leak with non-native
-		// event in IE.
-		handle.elem = elem;
-			
-			// Handle multiple events seperated by a space
-			// jQuery(...).bind("mouseover mouseout", fn);
-			jQuery.each(types.split(/\s+/), function(index, type) {
-				// Namespaced event handlers
-				var parts = type.split(".");
-				type = parts[0];
-				handler.type = parts[1];
-
-				// Get the current list of functions bound to this event
-				var handlers = events[type];
-
-				// Init the event handler queue
-				if (!handlers) {
-					handlers = events[type] = {};
-		
-					// Check for a special event handler
-					// Only use addEventListener/attachEvent if the special
-					// events handler returns false
-					if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) {
-						// Bind the global event handler to the element
-						if (elem.addEventListener)
-							elem.addEventListener(type, handle, false);
-						else if (elem.attachEvent)
-							elem.attachEvent("on" + type, handle);
-					}
-				}
-
-				// Add the function to the element's handler list
-				handlers[handler.guid] = handler;
-
-				// Keep track of which events have been used, for global triggering
-				jQuery.event.global[type] = true;
-			});
-		
-		// Nullify elem to prevent memory leaks in IE
-		elem = null;
-	},
-
-	guid: 1,
-	global: {},
-
-	// Detach an event or set of events from an element
-	remove: function(elem, types, handler) {
-		// don't do events on text and comment nodes
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		var events = jQuery.data(elem, "events"), ret, index;
-
-		if ( events ) {
-			// Unbind all events for the element
-			if ( types == undefined || (typeof types == "string" && types.charAt(0) == ".") )
-				for ( var type in events )
-					this.remove( elem, type + (types || "") );
-			else {
-				// types is actually an event object here
-				if ( types.type ) {
-					handler = types.handler;
-					types = types.type;
-				}
-				
-				// Handle multiple events seperated by a space
-				// jQuery(...).unbind("mouseover mouseout", fn);
-				jQuery.each(types.split(/\s+/), function(index, type){
-					// Namespaced event handlers
-					var parts = type.split(".");
-					type = parts[0];
-					
-					if ( events[type] ) {
-						// remove the given handler for the given type
-						if ( handler )
-							delete events[type][handler.guid];
-			
-						// remove all handlers for the given type
-						else
-							for ( handler in events[type] )
-								// Handle the removal of namespaced events
-								if ( !parts[1] || events[type][handler].type == parts[1] )
-									delete events[type][handler];
-
-						// remove generic event handler if no more handlers exist
-						for ( ret in events[type] ) break;
-						if ( !ret ) {
-							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) {
-								if (elem.removeEventListener)
-									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
-								else if (elem.detachEvent)
-									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
-							}
-							ret = null;
-							delete events[type];
-						}
-					}
-				});
-			}
-
-			// Remove the expando if it's no longer used
-			for ( ret in events ) break;
-			if ( !ret ) {
-				var handle = jQuery.data( elem, "handle" );
-				if ( handle ) handle.elem = null;
-				jQuery.removeData( elem, "events" );
-				jQuery.removeData( elem, "handle" );
-			}
-		}
-	},
-
-	trigger: function(type, data, elem, donative, extra) {
-		// Clone the incoming data, if any
-		data = jQuery.makeArray(data || []);
-
-		if ( type.indexOf("!") >= 0 ) {
-			type = type.slice(0, -1);
-			var exclusive = true;
-		}
-
-		// Handle a global trigger
-		if ( !elem ) {
-			// Only trigger if we've ever bound an event for it
-			if ( this.global[type] )
-				jQuery("*").add([window, document]).trigger(type, data);
-
-		// Handle triggering a single element
-		} else {
-			// don't do events on text and comment nodes
-			if ( elem.nodeType == 3 || elem.nodeType == 8 )
-				return undefined;
-
-			var val, ret, fn = jQuery.isFunction( elem[ type ] || null ),
-				// Check to see if we need to provide a fake event, or not
-				event = !data[0] || !data[0].preventDefault;
-			
-			// Pass along a fake event
-			if ( event )
-				data.unshift( this.fix({ type: type, target: elem }) );
-
-			// Enforce the right trigger type
-			data[0].type = type;
-			if ( exclusive )
-				data[0].exclusive = true;
-
-			// Trigger the event
-			if ( jQuery.isFunction( jQuery.data(elem, "handle") ) )
-				val = jQuery.data(elem, "handle").apply( elem, data );
-
-			// Handle triggering native .onfoo handlers
-			if ( !fn && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
-				val = false;
-
-			// Extra functions don't get the custom event object
-			if ( event )
-				data.shift();
-
-			// Handle triggering of extra function
-			if ( extra && jQuery.isFunction( extra ) ) {
-				// call the extra function and tack the current return value on the end for possible inspection
-				ret = extra.apply( elem, val == null ? data : data.concat( val ) );
-				// if anything is returned, give it precedence and have it overwrite the previous value
-				if (ret !== undefined)
-					val = ret;
-			}
-
-			// Trigger the native events (except for clicks on links)
-			if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
-				this.triggered = true;
-				try {
-					elem[ type ]();
-				// prevent IE from throwing an error for some hidden elements
-				} catch (e) {}
-			}
-
-			this.triggered = false;
-		}
-
-		return val;
-	},
-
-	handle: function(event) {
-		// returned undefined or false
-		var val;
-
-		// Empty object is for triggered events with no data
-		event = jQuery.event.fix( event || window.event || {} ); 
-
-		// Namespaced event handlers
-		var parts = event.type.split(".");
-		event.type = parts[0];
-
-		var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
-		args.unshift( event );
-
-		for ( var j in handlers ) {
-			var handler = handlers[j];
-			// Pass in a reference to the handler function itself
-			// So that we can later remove it
-			args[0].handler = handler;
-			args[0].data = handler.data;
-
-			// Filter the functions by class
-			if ( !parts[1] && !event.exclusive || handler.type == parts[1] ) {
-				var ret = handler.apply( this, args );
-
-				if ( val !== false )
-					val = ret;
-
-				if ( ret === false ) {
-					event.preventDefault();
-					event.stopPropagation();
-				}
-			}
-		}
-
-		// Clean up added properties in IE to prevent memory leak
-		if (jQuery.browser.msie)
-			event.target = event.preventDefault = event.stopPropagation =
-				event.handler = event.data = null;
-
-		return val;
-	},
-
-	fix: function(event) {
-		// store a copy of the original event object 
-		// and clone to set read-only properties
-		var originalEvent = event;
-		event = jQuery.extend({}, originalEvent);
-		
-		// add preventDefault and stopPropagation since 
-		// they will not work on the clone
-		event.preventDefault = function() {
-			// if preventDefault exists run it on the original event
-			if (originalEvent.preventDefault)
-				originalEvent.preventDefault();
-			// otherwise set the returnValue property of the original event to false (IE)
-			originalEvent.returnValue = false;
-		};
-		event.stopPropagation = function() {
-			// if stopPropagation exists run it on the original event
-			if (originalEvent.stopPropagation)
-				originalEvent.stopPropagation();
-			// otherwise set the cancelBubble property of the original event to true (IE)
-			originalEvent.cancelBubble = true;
-		};
-		
-		// Fix target property, if necessary
-		if ( !event.target )
-			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
-				
-		// check if target is a textnode (safari)
-		if ( event.target.nodeType == 3 )
-			event.target = originalEvent.target.parentNode;
-
-		// Add relatedTarget, if necessary
-		if ( !event.relatedTarget && event.fromElement )
-			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
-
-		// Calculate pageX/Y if missing and clientX/Y available
-		if ( event.pageX == null && event.clientX != null ) {
-			var doc = document.documentElement, body = document.body;
-			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
-			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
-		}
-			
-		// Add which for key events
-		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
-			event.which = event.charCode || event.keyCode;
-		
-		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
-		if ( !event.metaKey && event.ctrlKey )
-			event.metaKey = event.ctrlKey;
-
-		// Add which for click: 1 == left; 2 == middle; 3 == right
-		// Note: button is not normalized, so don't use it
-		if ( !event.which && event.button )
-			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
-			
-		return event;
-	},
-	
-	special: {
-		ready: {
-			setup: function() {
-				// Make sure the ready event is setup
-				bindReady();
-				return;
-			},
-			
-			teardown: function() { return; }
-		},
-		
-		mouseenter: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseenter
-				arguments[0].type = "mouseenter";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		},
-	
-		mouseleave: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseleave
-				arguments[0].type = "mouseleave";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		}
-	}
-};
-
-jQuery.fn.extend({
-	bind: function( type, data, fn ) {
-		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
-			jQuery.event.add( this, type, fn || data, fn && data );
-		});
-	},
-	
-	one: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.add( this, type, function(event) {
-				jQuery(this).unbind(event);
-				return (fn || data).apply( this, arguments);
-			}, fn && data);
-		});
-	},
-
-	unbind: function( type, fn ) {
-		return this.each(function(){
-			jQuery.event.remove( this, type, fn );
-		});
-	},
-
-	trigger: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.trigger( type, data, this, true, fn );
-		});
-	},
-
-	triggerHandler: function( type, data, fn ) {
-		if ( this[0] )
-			return jQuery.event.trigger( type, data, this[0], false, fn );
-		return undefined;
-	},
-
-	toggle: function() {
-		// Save reference to arguments for access in closure
-		var args = arguments;
-
-		return this.click(function(event) {
-			// Figure out which function to execute
-			this.lastToggle = 0 == this.lastToggle ? 1 : 0;
-			
-			// Make sure that clicks stop
-			event.preventDefault();
-			
-			// and execute the function
-			return args[this.lastToggle].apply( this, arguments ) || false;
-		});
-	},
-
-	hover: function(fnOver, fnOut) {
-		return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
-	},
-	
-	ready: function(fn) {
-		// Attach the listeners
-		bindReady();
-
-		// If the DOM is already ready
-		if ( jQuery.isReady )
-			// Execute the function immediately
-			fn.call( document, jQuery );
-			
-		// Otherwise, remember the function for later
-		else
-			// Add the function to the wait list
-			jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
-	
-		return this;
-	}
-});
-
-jQuery.extend({
-	isReady: false,
-	readyList: [],
-	// Handle when the DOM is ready
-	ready: function() {
-		// Make sure that the DOM is not already loaded
-		if ( !jQuery.isReady ) {
-			// Remember that the DOM is ready
-			jQuery.isReady = true;
-			
-			// If there are functions bound, to execute
-			if ( jQuery.readyList ) {
-				// Execute all of them
-				jQuery.each( jQuery.readyList, function(){
-					this.apply( document );
-				});
-				
-				// Reset the list of functions
-				jQuery.readyList = null;
-			}
-		
-			// Trigger any bound ready events
-			jQuery(document).triggerHandler("ready");
-		}
-	}
-});
-
-var readyBound = false;
-
-function bindReady(){
-	if ( readyBound ) return;
-	readyBound = true;
-
-	// Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
-	if ( document.addEventListener && !jQuery.browser.opera)
-		// Use the handy event callback
-		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
-	
-	// If IE is used and is not in a frame
-	// Continually check to see if the document is ready
-	if ( jQuery.browser.msie && window == top ) (function(){
-		if (jQuery.isReady) return;
-		try {
-			// If IE is used, use the trick by Diego Perini
-			// http://javascript.nwbox.com/IEContentLoaded/
-			document.documentElement.doScroll("left");
-		} catch( error ) {
-			setTimeout( arguments.callee, 0 );
-			return;
-		}
-		// and execute any waiting functions
-		jQuery.ready();
-	})();
-
-	if ( jQuery.browser.opera )
-		document.addEventListener( "DOMContentLoaded", function () {
-			if (jQuery.isReady) return;
-			for (var i = 0; i < document.styleSheets.length; i++)
-				if (document.styleSheets[i].disabled) {
-					setTimeout( arguments.callee, 0 );
-					return;
-				}
-			// and execute any waiting functions
-			jQuery.ready();
-		}, false);
-
-	if ( jQuery.browser.safari ) {
-		var numStyles;
-		(function(){
-			if (jQuery.isReady) return;
-			if ( document.readyState != "loaded" && document.readyState != "complete" ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			if ( numStyles === undefined )
-				numStyles = jQuery("style, link[rel=stylesheet]").length;
-			if ( document.styleSheets.length != numStyles ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			// and execute any waiting functions
-			jQuery.ready();
-		})();
-	}
-
-	// A fallback to window.onload, that will always work
-	jQuery.event.add( window, "load", jQuery.ready );
-}
-
-jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
-	"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
-	"submit,keydown,keypress,keyup,error").split(","), function(i, name){
-	
-	// Handle event binding
-	jQuery.fn[name] = function(fn){
-		return fn ? this.bind(name, fn) : this.trigger(name);
-	};
-});
-
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function(event, elem) {
-	// Check if mouse(over|out) are still within the same parent element
-	var parent = event.relatedTarget;
-	// Traverse up the tree
-	while ( parent && parent != elem ) try { parent = parent.parentNode; } catch(error) { parent = elem; }
-	// Return true if we actually just moused on to a sub-element
-	return parent == elem;
-};
-
-// Prevent memory leaks in IE
-// And prevent errors on refresh with events like mouseover in other browsers
-// Window isn't included so as not to unbind existing unload events
-jQuery(window).bind("unload", function() {
-	jQuery("*").add(document).unbind();
-});
-jQuery.fn.extend({
-	load: function( url, params, callback ) {
-		if ( jQuery.isFunction( url ) )
-			return this.bind("load", url);
-
-		var off = url.indexOf(" ");
-		if ( off >= 0 ) {
-			var selector = url.slice(off, url.length);
-			url = url.slice(0, off);
-		}
-
-		callback = callback || function(){};
-
-		// Default to a GET request
-		var type = "GET";
-
-		// If the second parameter was provided
-		if ( params )
-			// If it's a function
-			if ( jQuery.isFunction( params ) ) {
-				// We assume that it's the callback
-				callback = params;
-				params = null;
-
-			// Otherwise, build a param string
-			} else {
-				params = jQuery.param( params );
-				type = "POST";
-			}
-
-		var self = this;
-
-		// Request the remote document
-		jQuery.ajax({
-			url: url,
-			type: type,
-			dataType: "html",
-			data: params,
-			complete: function(res, status){
-				// If successful, inject the HTML into all the matched elements
-				if ( status == "success" || status == "notmodified" )
-					// See if a selector was specified
-					self.html( selector ?
-						// Create a dummy div to hold the results
-						jQuery("<div/>")
-							// inject the contents of the document in, removing the scripts
-							// to avoid any 'Permission Denied' errors in IE
-							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
-
-							// Locate the specified elements
-							.find(selector) :
-
-						// If not, just inject the full result
-						res.responseText );
-
-				self.each( callback, [res.responseText, status, res] );
-			}
-		});
-		return this;
-	},
-
-	serialize: function() {
-		return jQuery.param(this.serializeArray());
-	},
-	serializeArray: function() {
-		return this.map(function(){
-			return jQuery.nodeName(this, "form") ?
-				jQuery.makeArray(this.elements) : this;
-		})
-		.filter(function(){
-			return this.name && !this.disabled && 
-				(this.checked || /select|textarea/i.test(this.nodeName) || 
-					/text|hidden|password/i.test(this.type));
-		})
-		.map(function(i, elem){
-			var val = jQuery(this).val();
-			return val == null ? null :
-				val.constructor == Array ?
-					jQuery.map( val, function(val, i){
-						return {name: elem.name, value: val};
-					}) :
-					{name: elem.name, value: val};
-		}).get();
-	}
-});
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
-	jQuery.fn[o] = function(f){
-		return this.bind(o, f);
-	};
-});
-
-var jsc = (new Date).getTime();
-
-jQuery.extend({
-	get: function( url, data, callback, type ) {
-		// shift arguments if data argument was ommited
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = null;
-		}
-		
-		return jQuery.ajax({
-			type: "GET",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	getScript: function( url, callback ) {
-		return jQuery.get(url, null, callback, "script");
-	},
-
-	getJSON: function( url, data, callback ) {
-		return jQuery.get(url, data, callback, "json");
-	},
-
-	post: function( url, data, callback, type ) {
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = {};
-		}
-
-		return jQuery.ajax({
-			type: "POST",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	ajaxSetup: function( settings ) {
-		jQuery.extend( jQuery.ajaxSettings, settings );
-	},
-
-	ajaxSettings: {
-		global: true,
-		type: "GET",
-		timeout: 0,
-		contentType: "application/x-www-form-urlencoded",
-		processData: true,
-		async: true,
-		data: null,
-		username: null,
-		password: null,
-		accepts: {
-			xml: "application/xml, text/xml",
-			html: "text/html",
-			script: "text/javascript, application/javascript",
-			json: "application/json, text/javascript",
-			text: "text/plain",
-			_default: "*/*"
-		}
-	},
-	
-	// Last-Modified header cache for next request
-	lastModified: {},
-
-	ajax: function( s ) {
-		var jsonp, jsre = /=\?(&|$)/g, status, data;
-
-		// Extend the settings, but re-extend 's' so that it can be
-		// checked again later (in the test suite, specifically)
-		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
-
-		// convert data if not already a string
-		if ( s.data && s.processData && typeof s.data != "string" )
-			s.data = jQuery.param(s.data);
-
-		// Handle JSONP Parameter Callbacks
-		if ( s.dataType == "jsonp" ) {
-			if ( s.type.toLowerCase() == "get" ) {
-				if ( !s.url.match(jsre) )
-					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
-			} else if ( !s.data || !s.data.match(jsre) )
-				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
-			s.dataType = "json";
-		}
-
-		// Build temporary JSONP function
-		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
-			jsonp = "jsonp" + jsc++;
-
-			// Replace the =? sequence both in the query string and the data
-			if ( s.data )
-				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
-			s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
-			// We need to make sure
-			// that a JSONP style response is executed properly
-			s.dataType = "script";
-
-			// Handle JSONP-style loading
-			window[ jsonp ] = function(tmp){
-				data = tmp;
-				success();
-				complete();
-				// Garbage collect
-				window[ jsonp ] = undefined;
-				try{ delete window[ jsonp ]; } catch(e){}
-				if ( head )
-					head.removeChild( script );
-			};
-		}
-
-		if ( s.dataType == "script" && s.cache == null )
-			s.cache = false;
-
-		if ( s.cache === false && s.type.toLowerCase() == "get" ) {
-			var ts = (new Date()).getTime();
-			// try replacing _= if it is there
-			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
-			// if nothing was replaced, add timestamp to the end
-			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
-		}
-
-		// If data is available, append data to url for get requests
-		if ( s.data && s.type.toLowerCase() == "get" ) {
-			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
-
-			// IE likes to send both get and post data, prevent this
-			s.data = null;
-		}
-
-		// Watch for a new set of requests
-		if ( s.global && ! jQuery.active++ )
-			jQuery.event.trigger( "ajaxStart" );
-
-		// If we're requesting a remote document
-		// and trying to load JSON or Script with a GET
-		if ( (!s.url.indexOf("http") || !s.url.indexOf("//")) && s.dataType == "script" && s.type.toLowerCase() == "get" ) {
-			var head = document.getElementsByTagName("head")[0];
-			var script = document.createElement("script");
-			script.src = s.url;
-			if (s.scriptCharset)
-				script.charset = s.scriptCharset;
-
-			// Handle Script loading
-			if ( !jsonp ) {
-				var done = false;
-
-				// Attach handlers for all browsers
-				script.onload = script.onreadystatechange = function(){
-					if ( !done && (!this.readyState || 
-							this.readyState == "loaded" || this.readyState == "complete") ) {
-						done = true;
-						success();
-						complete();
-						head.removeChild( script );
-					}
-				};
-			}
-
-			head.appendChild(script);
-
-			// We handle everything using the script element injection
-			return undefined;
-		}
-
-		var requestDone = false;
-
-		// Create the request object; Microsoft failed to properly
-		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
-		var xml = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
-
-		// Open the socket
-		xml.open(s.type, s.url, s.async, s.username, s.password);
-
-		// Need an extra try/catch for cross domain requests in Firefox 3
-		try {
-			// Set the correct header, if data is being sent
-			if ( s.data )
-				xml.setRequestHeader("Content-Type", s.contentType);
-
-			// Set the If-Modified-Since header, if ifModified mode.
-			if ( s.ifModified )
-				xml.setRequestHeader("If-Modified-Since",
-					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
-
-			// Set header so the called script knows that it's an XMLHttpRequest
-			xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-
-			// Set the Accepts header for the server, depending on the dataType
-			xml.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
-				s.accepts[ s.dataType ] + ", */*" :
-				s.accepts._default );
-		} catch(e){}
-
-		// Allow custom headers/mimetypes
-		if ( s.beforeSend )
-			s.beforeSend(xml);
-			
-		if ( s.global )
-			jQuery.event.trigger("ajaxSend", [xml, s]);
-
-		// Wait for a response to come back
-		var onreadystatechange = function(isTimeout){
-			// The transfer is complete and the data is available, or the request timed out
-			if ( !requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
-				requestDone = true;
-				
-				// clear poll interval
-				if (ival) {
-					clearInterval(ival);
-					ival = null;
-				}
-				
-				status = isTimeout == "timeout" && "timeout" ||
-					!jQuery.httpSuccess( xml ) && "error" ||
-					s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
-					"success";
-
-				if ( status == "success" ) {
-					// Watch for, and catch, XML document parse errors
-					try {
-						// process the data (runs the xml through httpData regardless of callback)
-						data = jQuery.httpData( xml, s.dataType );
-					} catch(e) {
-						status = "parsererror";
-					}
-				}
-
-				// Make sure that the request was successful or notmodified
-				if ( status == "success" ) {
-					// Cache Last-Modified header, if ifModified mode.
-					var modRes;
-					try {
-						modRes = xml.getResponseHeader("Last-Modified");
-					} catch(e) {} // swallow exception thrown by FF if header is not available
-	
-					if ( s.ifModified && modRes )
-						jQuery.lastModified[s.url] = modRes;
-
-					// JSONP handles its own success callback
-					if ( !jsonp )
-						success();	
-				} else
-					jQuery.handleError(s, xml, status);
-
-				// Fire the complete handlers
-				complete();
-
-				// Stop memory leaks
-				if ( s.async )
-					xml = null;
-			}
-		};
-		
-		if ( s.async ) {
-			// don't attach the handler to the request, just poll it instead
-			var ival = setInterval(onreadystatechange, 13); 
-
-			// Timeout checker
-			if ( s.timeout > 0 )
-				setTimeout(function(){
-					// Check to see if the request is still happening
-					if ( xml ) {
-						// Cancel the request
-						xml.abort();
-	
-						if( !requestDone )
-							onreadystatechange( "timeout" );
-					}
-				}, s.timeout);
-		}
-			
-		// Send the data
-		try {
-			xml.send(s.data);
-		} catch(e) {
-			jQuery.handleError(s, xml, null, e);
-		}
-		
-		// firefox 1.5 doesn't fire statechange for sync requests
-		if ( !s.async )
-			onreadystatechange();
-
-		function success(){
-			// If a local callback was specified, fire it and pass it the data
-			if ( s.success )
-				s.success( data, status );
-
-			// Fire the global callback
-			if ( s.global )
-				jQuery.event.trigger( "ajaxSuccess", [xml, s] );
-		}
-
-		function complete(){
-			// Process result
-			if ( s.complete )
-				s.complete(xml, status);
-
-			// The request was completed
-			if ( s.global )
-				jQuery.event.trigger( "ajaxComplete", [xml, s] );
-
-			// Handle the global AJAX counter
-			if ( s.global && ! --jQuery.active )
-				jQuery.event.trigger( "ajaxStop" );
-		}
-		
-		// return XMLHttpRequest to allow aborting the request etc.
-		return xml;
-	},
-
-	handleError: function( s, xml, status, e ) {
-		// If a local callback was specified, fire it
-		if ( s.error ) s.error( xml, status, e );
-
-		// Fire the global callback
-		if ( s.global )
-			jQuery.event.trigger( "ajaxError", [xml, s, e] );
-	},
-
-	// Counter for holding the number of active queries
-	active: 0,
-
-	// Determines if an XMLHttpRequest was successful or not
-	httpSuccess: function( r ) {
-		try {
-			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
-			return !r.status && location.protocol == "file:" ||
-				( r.status >= 200 && r.status < 300 ) || r.status == 304 || r.status == 1223 ||
-				jQuery.browser.safari && r.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	// Determines if an XMLHttpRequest returns NotModified
-	httpNotModified: function( xml, url ) {
-		try {
-			var xmlRes = xml.getResponseHeader("Last-Modified");
-
-			// Firefox always returns 200. check Last-Modified date
-			return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
-				jQuery.browser.safari && xml.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	httpData: function( r, type ) {
-		var ct = r.getResponseHeader("content-type");
-		var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
-		var data = xml ? r.responseXML : r.responseText;
-
-		if ( xml && data.documentElement.tagName == "parsererror" )
-			throw "parsererror";
-
-		// If the type is "script", eval it in global context
-		if ( type == "script" )
-			jQuery.globalEval( data );
-
-		// Get the JavaScript object, if JSON is used.
-		if ( type == "json" )
-			data = eval("(" + data + ")");
-
-		return data;
-	},
-
-	// Serialize an array of form elements or a set of
-	// key/values into a query string
-	param: function( a ) {
-		var s = [];
-
-		// If an array was passed in, assume that it is an array
-		// of form elements
-		if ( a.constructor == Array || a.jquery )
-			// Serialize the form elements
-			jQuery.each( a, function(){
-				s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
-			});
-
-		// Otherwise, assume that it's an object of key/value pairs
-		else
-			// Serialize the key/values
-			for ( var j in a )
-				// If the value is an array then the key names need to be repeated
-				if ( a[j] && a[j].constructor == Array )
-					jQuery.each( a[j], function(){
-						s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
-					});
-				else
-					s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
-
-		// Return the resulting serialization
-		return s.join("&").replace(/%20/g, "+");
-	}
-
-});
-jQuery.fn.extend({
-	show: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "show", width: "show", opacity: "show"
-			}, speed, callback) :
-			
-			this.filter(":hidden").each(function(){
-				this.style.display = this.oldblock || "";
-				if ( jQuery.css(this,"display") == "none" ) {
-					var elem = jQuery("<" + this.tagName + " />").appendTo("body");
-					this.style.display = elem.css("display");
-					// handle an edge condition where css is - div { display:none; } or similar
-					if (this.style.display == "none")
-						this.style.display = "block";
-					elem.remove();
-				}
-			}).end();
-	},
-	
-	hide: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "hide", width: "hide", opacity: "hide"
-			}, speed, callback) :
-			
-			this.filter(":visible").each(function(){
-				this.oldblock = this.oldblock || jQuery.css(this,"display");
-				this.style.display = "none";
-			}).end();
-	},
-
-	// Save the old toggle function
-	_toggle: jQuery.fn.toggle,
-	
-	toggle: function( fn, fn2 ){
-		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
-			this._toggle( fn, fn2 ) :
-			fn ?
-				this.animate({
-					height: "toggle", width: "toggle", opacity: "toggle"
-				}, fn, fn2) :
-				this.each(function(){
-					jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
-				});
-	},
-	
-	slideDown: function(speed,callback){
-		return this.animate({height: "show"}, speed, callback);
-	},
-	
-	slideUp: function(speed,callback){
-		return this.animate({height: "hide"}, speed, callback);
-	},
-
-	slideToggle: function(speed, callback){
-		return this.animate({height: "toggle"}, speed, callback);
-	},
-	
-	fadeIn: function(speed, callback){
-		return this.animate({opacity: "show"}, speed, callback);
-	},
-	
-	fadeOut: function(speed, callback){
-		return this.animate({opacity: "hide"}, speed, callback);
-	},
-	
-	fadeTo: function(speed,to,callback){
-		return this.animate({opacity: to}, speed, callback);
-	},
-	
-	animate: function( prop, speed, easing, callback ) {
-		var optall = jQuery.speed(speed, easing, callback);
-
-		return this[ optall.queue === false ? "each" : "queue" ](function(){
-			if ( this.nodeType != 1)
-				return false;
-
-			var opt = jQuery.extend({}, optall);
-			var hidden = jQuery(this).is(":hidden"), self = this;
-			
-			for ( var p in prop ) {
-				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
-					return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
-
-				if ( p == "height" || p == "width" ) {
-					// Store display property
-					opt.display = jQuery.css(this, "display");
-
-					// Make sure that nothing sneaks out
-					opt.overflow = this.style.overflow;
-				}
-			}
-
-			if ( opt.overflow != null )
-				this.style.overflow = "hidden";
-
-			opt.curAnim = jQuery.extend({}, prop);
-			
-			jQuery.each( prop, function(name, val){
-				var e = new jQuery.fx( self, opt, name );
-
-				if ( /toggle|show|hide/.test(val) )
-					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
-				else {
-					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
-						start = e.cur(true) || 0;
-
-					if ( parts ) {
-						var end = parseFloat(parts[2]),
-							unit = parts[3] || "px";
-
-						// We need to compute starting value
-						if ( unit != "px" ) {
-							self.style[ name ] = (end || 1) + unit;
-							start = ((end || 1) / e.cur(true)) * start;
-							self.style[ name ] = start + unit;
-						}
-
-						// If a +=/-= token was provided, we're doing a relative animation
-						if ( parts[1] )
-							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
-
-						e.custom( start, end, unit );
-					} else
-						e.custom( start, val, "" );
-				}
-			});
-
-			// For JS strict compliance
-			return true;
-		});
-	},
-	
-	queue: function(type, fn){
-		if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
-			fn = type;
-			type = "fx";
-		}
-
-		if ( !type || (typeof type == "string" && !fn) )
-			return queue( this[0], type );
-
-		return this.each(function(){
-			if ( fn.constructor == Array )
-				queue(this, type, fn);
-			else {
-				queue(this, type).push( fn );
-			
-				if ( queue(this, type).length == 1 )
-					fn.apply(this);
-			}
-		});
-	},
-
-	stop: function(clearQueue, gotoEnd){
-		var timers = jQuery.timers;
-
-		if (clearQueue)
-			this.queue([]);
-
-		this.each(function(){
-			// go in reverse order so anything added to the queue during the loop is ignored
-			for ( var i = timers.length - 1; i >= 0; i-- )
-				if ( timers[i].elem == this ) {
-					if (gotoEnd)
-						// force the next step to be the last
-						timers[i](true);
-					timers.splice(i, 1);
-				}
-		});
-
-		// start the next in the queue if the last step wasn't forced
-		if (!gotoEnd)
-			this.dequeue();
-
-		return this;
-	}
-
-});
-
-var queue = function( elem, type, array ) {
-	if ( !elem )
-		return undefined;
-
-	type = type || "fx";
-
-	var q = jQuery.data( elem, type + "queue" );
-
-	if ( !q || array )
-		q = jQuery.data( elem, type + "queue", 
-			array ? jQuery.makeArray(array) : [] );
-
-	return q;
-};
-
-jQuery.fn.dequeue = function(type){
-	type = type || "fx";
-
-	return this.each(function(){
-		var q = queue(this, type);
-
-		q.shift();
-
-		if ( q.length )
-			q[0].apply( this );
-	});
-};
-
-jQuery.extend({
-	
-	speed: function(speed, easing, fn) {
-		var opt = speed && speed.constructor == Object ? speed : {
-			complete: fn || !fn && easing || 
-				jQuery.isFunction( speed ) && speed,
-			duration: speed,
-			easing: fn && easing || easing && easing.constructor != Function && easing
-		};
-
-		opt.duration = (opt.duration && opt.duration.constructor == Number ? 
-			opt.duration : 
-			{ slow: 600, fast: 200 }[opt.duration]) || 400;
-	
-		// Queueing
-		opt.old = opt.complete;
-		opt.complete = function(){
-			if ( opt.queue !== false )
-				jQuery(this).dequeue();
-			if ( jQuery.isFunction( opt.old ) )
-				opt.old.apply( this );
-		};
-	
-		return opt;
-	},
-	
-	easing: {
-		linear: function( p, n, firstNum, diff ) {
-			return firstNum + diff * p;
-		},
-		swing: function( p, n, firstNum, diff ) {
-			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
-		}
-	},
-	
-	timers: [],
-	timerId: null,
-
-	fx: function( elem, options, prop ){
-		this.options = options;
-		this.elem = elem;
-		this.prop = prop;
-
-		if ( !options.orig )
-			options.orig = {};
-	}
-
-});
-
-jQuery.fx.prototype = {
-
-	// Simple function for setting a style value
-	update: function(){
-		if ( this.options.step )
-			this.options.step.apply( this.elem, [ this.now, this ] );
-
-		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
-
-		// Set display property to block for height/width animations
-		if ( this.prop == "height" || this.prop == "width" )
-			this.elem.style.display = "block";
-	},
-
-	// Get the current size
-	cur: function(force){
-		if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
-			return this.elem[ this.prop ];
-
-		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
-		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
-	},
-
-	// Start an animation from one number to another
-	custom: function(from, to, unit){
-		this.startTime = (new Date()).getTime();
-		this.start = from;
-		this.end = to;
-		this.unit = unit || this.unit || "px";
-		this.now = this.start;
-		this.pos = this.state = 0;
-		this.update();
-
-		var self = this;
-		function t(gotoEnd){
-			return self.step(gotoEnd);
-		}
-
-		t.elem = this.elem;
-
-		jQuery.timers.push(t);
-
-		if ( jQuery.timerId == null ) {
-			jQuery.timerId = setInterval(function(){
-				var timers = jQuery.timers;
-				
-				for ( var i = 0; i < timers.length; i++ )
-					if ( !timers[i]() )
-						timers.splice(i--, 1);
-
-				if ( !timers.length ) {
-					clearInterval( jQuery.timerId );
-					jQuery.timerId = null;
-				}
-			}, 13);
-		}
-	},
-
-	// Simple 'show' function
-	show: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.show = true;
-
-		// Begin the animation
-		this.custom(0, this.cur());
-
-		// Make sure that we start at a small width/height to avoid any
-		// flash of content
-		if ( this.prop == "width" || this.prop == "height" )
-			this.elem.style[this.prop] = "1px";
-		
-		// Start by showing the element
-		jQuery(this.elem).show();
-	},
-
-	// Simple 'hide' function
-	hide: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.hide = true;
-
-		// Begin the animation
-		this.custom(this.cur(), 0);
-	},
-
-	// Each step of an animation
-	step: function(gotoEnd){
-		var t = (new Date()).getTime();
-
-		if ( gotoEnd || t > this.options.duration + this.startTime ) {
-			this.now = this.end;
-			this.pos = this.state = 1;
-			this.update();
-
-			this.options.curAnim[ this.prop ] = true;
-
-			var done = true;
-			for ( var i in this.options.curAnim )
-				if ( this.options.curAnim[i] !== true )
-					done = false;
-
-			if ( done ) {
-				if ( this.options.display != null ) {
-					// Reset the overflow
-					this.elem.style.overflow = this.options.overflow;
-				
-					// Reset the display
-					this.elem.style.display = this.options.display;
-					if ( jQuery.css(this.elem, "display") == "none" )
-						this.elem.style.display = "block";
-				}
-
-				// Hide the element if the "hide" operation was done
-				if ( this.options.hide )
-					this.elem.style.display = "none";
-
-				// Reset the properties, if the item has been hidden or shown
-				if ( this.options.hide || this.options.show )
-					for ( var p in this.options.curAnim )
-						jQuery.attr(this.elem.style, p, this.options.orig[p]);
-			}
-
-			// If a callback was provided, execute it
-			if ( done && jQuery.isFunction( this.options.complete ) )
-				// Execute the complete function
-				this.options.complete.apply( this.elem );
-
-			return false;
-		} else {
-			var n = t - this.startTime;
-			this.state = n / this.options.duration;
-
-			// Perform the easing function, defaults to swing
-			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
-			this.now = this.start + ((this.end - this.start) * this.pos);
-
-			// Perform the next step of the animation
-			this.update();
-		}
-
-		return true;
-	}
-
-};
-
-jQuery.fx.step = {
-	scrollLeft: function(fx){
-		fx.elem.scrollLeft = fx.now;
-	},
-
-	scrollTop: function(fx){
-		fx.elem.scrollTop = fx.now;
-	},
-
-	opacity: function(fx){
-		jQuery.attr(fx.elem.style, "opacity", fx.now);
-	},
-
-	_default: function(fx){
-		fx.elem.style[ fx.prop ] = fx.now + fx.unit;
-	}
-};
-// The Offset Method
-// Originally By Brandon Aaron, part of the Dimension Plugin
-// http://jquery.com/plugins/project/dimensions
-jQuery.fn.offset = function() {
-	var left = 0, top = 0, elem = this[0], results;
-	
-	if ( elem ) with ( jQuery.browser ) {
-		var parent       = elem.parentNode, 
-		    offsetChild  = elem,
-		    offsetParent = elem.offsetParent, 
-		    doc          = elem.ownerDocument,
-		    safari2      = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
-		    fixed        = jQuery.css(elem, "position") == "fixed";
-	
-		// Use getBoundingClientRect if available
-		if ( elem.getBoundingClientRect ) {
-			var box = elem.getBoundingClientRect();
-		
-			// Add the document scroll offsets
-			add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-				box.top  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		
-			// IE adds the HTML element's border, by default it is medium which is 2px
-			// IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; }
-			// IE 7 standards mode, the border is always 2px
-			// This border/offset is typically represented by the clientLeft and clientTop properties
-			// However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
-			// Therefore this method will be off by 2px in IE while in quirksmode
-			add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
-	
-		// Otherwise loop through the offsetParents and parentNodes
-		} else {
-		
-			// Initial element offsets
-			add( elem.offsetLeft, elem.offsetTop );
-			
-			// Get parent offsets
-			while ( offsetParent ) {
-				// Add offsetParent offsets
-				add( offsetParent.offsetLeft, offsetParent.offsetTop );
-			
-				// Mozilla and Safari > 2 does not include the border on offset parents
-				// However Mozilla adds the border for table or table cells
-				if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
-					border( offsetParent );
-					
-				// Add the document scroll offsets if position is fixed on any offsetParent
-				if ( !fixed && jQuery.css(offsetParent, "position") == "fixed" )
-					fixed = true;
-			
-				// Set offsetChild to previous offsetParent unless it is the body element
-				offsetChild  = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
-				// Get next offsetParent
-				offsetParent = offsetParent.offsetParent;
-			}
-		
-			// Get parent scroll offsets
-			while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) ) {
-				// Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
-				if ( !/^inline|table.*$/i.test(jQuery.css(parent, "display")) )
-					// Subtract parent scroll offsets
-					add( -parent.scrollLeft, -parent.scrollTop );
-			
-				// Mozilla does not add the border for a parent that has overflow != visible
-				if ( mozilla && jQuery.css(parent, "overflow") != "visible" )
-					border( parent );
-			
-				// Get next parent
-				parent = parent.parentNode;
-			}
-		
-			// Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
-			// Mozilla doubles body offsets with a non-absolutely positioned offsetChild
-			if ( (safari2 && (fixed || jQuery.css(offsetChild, "position") == "absolute")) || 
-				(mozilla && jQuery.css(offsetChild, "position") != "absolute") )
-					add( -doc.body.offsetLeft, -doc.body.offsetTop );
-			
-			// Add the document scroll offsets if position is fixed
-			if ( fixed )
-				add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-					Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		}
-
-		// Return an object with top and left properties
-		results = { top: top, left: left };
-	}
-
-	function border(elem) {
-		add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) );
-	}
-
-	function add(l, t) {
-		left += parseInt(l) || 0;
-		top += parseInt(t) || 0;
-	}
-
-	return results;
-};
-})();
@@ -1,3408 +0,0 @@
-(function(){
-/*
- * jQuery 1.2.3 - New Wave Javascript
- *
- * Copyright (c) 2008 John Resig (jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
- * $Rev: 4663 $
- */
-
-// Map over jQuery in case of overwrite
-if ( window.jQuery )
-	var _jQuery = window.jQuery;
-
-var jQuery = window.jQuery = function( selector, context ) {
-	// The jQuery object is actually just the init constructor 'enhanced'
-	return new jQuery.prototype.init( selector, context );
-};
-
-// Map over the $ in case of overwrite
-if ( window.$ )
-	var _$ = window.$;
-	
-// Map the jQuery namespace to the '$' one
-window.$ = jQuery;
-
-// A simple way to check for HTML strings or ID strings
-// (both of which we optimize for)
-var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
-
-// Is it a simple selector
-var isSimple = /^.[^:#\[\.]*$/;
-
-jQuery.fn = jQuery.prototype = {
-	init: function( selector, context ) {
-		// Make sure that a selection was provided
-		selector = selector || document;
-
-		// Handle $(DOMElement)
-		if ( selector.nodeType ) {
-			this[0] = selector;
-			this.length = 1;
-			return this;
-
-		// Handle HTML strings
-		} else if ( typeof selector == "string" ) {
-			// Are we dealing with HTML string or an ID?
-			var match = quickExpr.exec( selector );
-
-			// Verify a match, and that no context was specified for #id
-			if ( match && (match[1] || !context) ) {
-
-				// HANDLE: $(html) -> $(array)
-				if ( match[1] )
-					selector = jQuery.clean( [ match[1] ], context );
-
-				// HANDLE: $("#id")
-				else {
-					var elem = document.getElementById( match[3] );
-
-					// Make sure an element was located
-					if ( elem )
-						// Handle the case where IE and Opera return items
-						// by name instead of ID
-						if ( elem.id != match[3] )
-							return jQuery().find( selector );
-
-						// Otherwise, we inject the element directly into the jQuery object
-						else {
-							this[0] = elem;
-							this.length = 1;
-							return this;
-						}
-
-					else
-						selector = [];
-				}
-
-			// HANDLE: $(expr, [context])
-			// (which is just equivalent to: $(content).find(expr)
-			} else
-				return new jQuery( context ).find( selector );
-
-		// HANDLE: $(function)
-		// Shortcut for document ready
-		} else if ( jQuery.isFunction( selector ) )
-			return new jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
-
-		return this.setArray(
-			// HANDLE: $(array)
-			selector.constructor == Array && selector ||
-
-			// HANDLE: $(arraylike)
-			// Watch for when an array-like object, contains DOM nodes, is passed in as the selector
-			(selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) ||
-
-			// HANDLE: $(*)
-			[ selector ] );
-	},
-	
-	// The current version of jQuery being used
-	jquery: "1.2.3",
-
-	// The number of elements contained in the matched element set
-	size: function() {
-		return this.length;
-	},
-	
-	// The number of elements contained in the matched element set
-	length: 0,
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-		return num == undefined ?
-
-			// Return a 'clean' array
-			jQuery.makeArray( this ) :
-
-			// Return just the object
-			this[ num ];
-	},
-	
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-		// Build a new jQuery matched element set
-		var ret = jQuery( elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-	
-	// Force the current matched set of elements to become
-	// the specified array of elements (destroying the stack in the process)
-	// You should use pushStack() in order to do this, but maintain the stack
-	setArray: function( elems ) {
-		// Resetting the length to 0, then using the native Array push
-		// is a super-fast way to populate an object with array-like properties
-		this.length = 0;
-		Array.prototype.push.apply( this, elems );
-		
-		return this;
-	},
-
-	// Execute a callback for every element in the matched set.
-	// (You can seed the arguments with an array of args, but this is
-	// only used internally.)
-	each: function( callback, args ) {
-		return jQuery.each( this, callback, args );
-	},
-
-	// Determine the position of an element within 
-	// the matched set of elements
-	index: function( elem ) {
-		var ret = -1;
-
-		// Locate the position of the desired element
-		this.each(function(i){
-			if ( this == elem )
-				ret = i;
-		});
-
-		return ret;
-	},
-
-	attr: function( name, value, type ) {
-		var options = name;
-		
-		// Look for the case where we're accessing a style value
-		if ( name.constructor == String )
-			if ( value == undefined )
-				return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined;
-
-			else {
-				options = {};
-				options[ name ] = value;
-			}
-		
-		// Check to see if we're setting style values
-		return this.each(function(i){
-			// Set all the styles
-			for ( name in options )
-				jQuery.attr(
-					type ?
-						this.style :
-						this,
-					name, jQuery.prop( this, options[ name ], type, i, name )
-				);
-		});
-	},
-
-	css: function( key, value ) {
-		// ignore negative width and height values
-		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
-			value = undefined;
-		return this.attr( key, value, "curCSS" );
-	},
-
-	text: function( text ) {
-		if ( typeof text != "object" && text != null )
-			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
-
-		var ret = "";
-
-		jQuery.each( text || this, function(){
-			jQuery.each( this.childNodes, function(){
-				if ( this.nodeType != 8 )
-					ret += this.nodeType != 1 ?
-						this.nodeValue :
-						jQuery.fn.text( [ this ] );
-			});
-		});
-
-		return ret;
-	},
-
-	wrapAll: function( html ) {
-		if ( this[0] )
-			// The elements to wrap the target around
-			jQuery( html, this[0].ownerDocument )
-				.clone()
-				.insertBefore( this[0] )
-				.map(function(){
-					var elem = this;
-
-					while ( elem.firstChild )
-						elem = elem.firstChild;
-
-					return elem;
-				})
-				.append(this);
-
-		return this;
-	},
-
-	wrapInner: function( html ) {
-		return this.each(function(){
-			jQuery( this ).contents().wrapAll( html );
-		});
-	},
-
-	wrap: function( html ) {
-		return this.each(function(){
-			jQuery( this ).wrapAll( html );
-		});
-	},
-
-	append: function() {
-		return this.domManip(arguments, true, false, function(elem){
-			if (this.nodeType == 1)
-				this.appendChild( elem );
-		});
-	},
-
-	prepend: function() {
-		return this.domManip(arguments, true, true, function(elem){
-			if (this.nodeType == 1)
-				this.insertBefore( elem, this.firstChild );
-		});
-	},
-	
-	before: function() {
-		return this.domManip(arguments, false, false, function(elem){
-			this.parentNode.insertBefore( elem, this );
-		});
-	},
-
-	after: function() {
-		return this.domManip(arguments, false, true, function(elem){
-			this.parentNode.insertBefore( elem, this.nextSibling );
-		});
-	},
-
-	end: function() {
-		return this.prevObject || jQuery( [] );
-	},
-
-	find: function( selector ) {
-		var elems = jQuery.map(this, function(elem){
-			return jQuery.find( selector, elem );
-		});
-
-		return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
-			jQuery.unique( elems ) :
-			elems );
-	},
-
-	clone: function( events ) {
-		// Do the clone
-		var ret = this.map(function(){
-			if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
-				// IE copies events bound via attachEvent when
-				// using cloneNode. Calling detachEvent on the
-				// clone will also remove the events from the orignal
-				// In order to get around this, we use innerHTML.
-				// Unfortunately, this means some modifications to 
-				// attributes in IE that are actually only stored 
-				// as properties will not be copied (such as the
-				// the name attribute on an input).
-				var clone = this.cloneNode(true),
-					container = document.createElement("div");
-				container.appendChild(clone);
-				return jQuery.clean([container.innerHTML])[0];
-			} else
-				return this.cloneNode(true);
-		});
-
-		// Need to set the expando to null on the cloned set if it exists
-		// removeData doesn't work here, IE removes it from the original as well
-		// this is primarily for IE but the data expando shouldn't be copied over in any browser
-		var clone = ret.find("*").andSelf().each(function(){
-			if ( this[ expando ] != undefined )
-				this[ expando ] = null;
-		});
-		
-		// Copy the events from the original to the clone
-		if ( events === true )
-			this.find("*").andSelf().each(function(i){
-				if (this.nodeType == 3)
-					return;
-				var events = jQuery.data( this, "events" );
-
-				for ( var type in events )
-					for ( var handler in events[ type ] )
-						jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
-			});
-
-		// Return the cloned set
-		return ret;
-	},
-
-	filter: function( selector ) {
-		return this.pushStack(
-			jQuery.isFunction( selector ) &&
-			jQuery.grep(this, function(elem, i){
-				return selector.call( elem, i );
-			}) ||
-
-			jQuery.multiFilter( selector, this ) );
-	},
-
-	not: function( selector ) {
-		if ( selector.constructor == String )
-			// test special case where just one selector is passed in
-			if ( isSimple.test( selector ) )
-				return this.pushStack( jQuery.multiFilter( selector, this, true ) );
-			else
-				selector = jQuery.multiFilter( selector, this );
-
-		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
-		return this.filter(function() {
-			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
-		});
-	},
-
-	add: function( selector ) {
-		return !selector ? this : this.pushStack( jQuery.merge( 
-			this.get(),
-			selector.constructor == String ? 
-				jQuery( selector ).get() :
-				selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ?
-					selector : [selector] ) );
-	},
-
-	is: function( selector ) {
-		return selector ?
-			jQuery.multiFilter( selector, this ).length > 0 :
-			false;
-	},
-
-	hasClass: function( selector ) {
-		return this.is( "." + selector );
-	},
-	
-	val: function( value ) {
-		if ( value == undefined ) {
-
-			if ( this.length ) {
-				var elem = this[0];
-
-				// We need to handle select boxes special
-				if ( jQuery.nodeName( elem, "select" ) ) {
-					var index = elem.selectedIndex,
-						values = [],
-						options = elem.options,
-						one = elem.type == "select-one";
-					
-					// Nothing was selected
-					if ( index < 0 )
-						return null;
-
-					// Loop through all the selected options
-					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
-						var option = options[ i ];
-
-						if ( option.selected ) {
-							// Get the specifc value for the option
-							value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
-							
-							// We don't need an array for one selects
-							if ( one )
-								return value;
-							
-							// Multi-Selects return an array
-							values.push( value );
-						}
-					}
-					
-					return values;
-					
-				// Everything else, we just grab the value
-				} else
-					return (this[0].value || "").replace(/\r/g, "");
-
-			}
-
-			return undefined;
-		}
-
-		return this.each(function(){
-			if ( this.nodeType != 1 )
-				return;
-
-			if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
-				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
-					jQuery.inArray(this.name, value) >= 0);
-
-			else if ( jQuery.nodeName( this, "select" ) ) {
-				var values = value.constructor == Array ?
-					value :
-					[ value ];
-
-				jQuery( "option", this ).each(function(){
-					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
-						jQuery.inArray( this.text, values ) >= 0);
-				});
-
-				if ( !values.length )
-					this.selectedIndex = -1;
-
-			} else
-				this.value = value;
-		});
-	},
-	
-	html: function( value ) {
-		return value == undefined ?
-			(this.length ?
-				this[0].innerHTML :
-				null) :
-			this.empty().append( value );
-	},
-
-	replaceWith: function( value ) {
-		return this.after( value ).remove();
-	},
-
-	eq: function( i ) {
-		return this.slice( i, i + 1 );
-	},
-
-	slice: function() {
-		return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map(this, function(elem, i){
-			return callback.call( elem, i, elem );
-		}));
-	},
-
-	andSelf: function() {
-		return this.add( this.prevObject );
-	},
-
-	data: function( key, value ){
-		var parts = key.split(".");
-		parts[1] = parts[1] ? "." + parts[1] : "";
-
-		if ( value == null ) {
-			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-			
-			if ( data == undefined && this.length )
-				data = jQuery.data( this[0], key );
-
-			return data == null && parts[1] ?
-				this.data( parts[0] ) :
-				data;
-		} else
-			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
-				jQuery.data( this, key, value );
-			});
-	},
-
-	removeData: function( key ){
-		return this.each(function(){
-			jQuery.removeData( this, key );
-		});
-	},
-	
-	domManip: function( args, table, reverse, callback ) {
-		var clone = this.length > 1, elems; 
-
-		return this.each(function(){
-			if ( !elems ) {
-				elems = jQuery.clean( args, this.ownerDocument );
-
-				if ( reverse )
-					elems.reverse();
-			}
-
-			var obj = this;
-
-			if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
-				obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
-
-			var scripts = jQuery( [] );
-
-			jQuery.each(elems, function(){
-				var elem = clone ?
-					jQuery( this ).clone( true )[0] :
-					this;
-
-				// execute all scripts after the elements have been injected
-				if ( jQuery.nodeName( elem, "script" ) ) {
-					scripts = scripts.add( elem );
-				} else {
-					// Remove any inner scripts for later evaluation
-					if ( elem.nodeType == 1 )
-						scripts = scripts.add( jQuery( "script", elem ).remove() );
-
-					// Inject the elements into the document
-					callback.call( obj, elem );
-				}
-			});
-
-			scripts.each( evalScript );
-		});
-	}
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.prototype.init.prototype = jQuery.prototype;
-
-function evalScript( i, elem ) {
-	if ( elem.src )
-		jQuery.ajax({
-			url: elem.src,
-			async: false,
-			dataType: "script"
-		});
-
-	else
-		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
-
-	if ( elem.parentNode )
-		elem.parentNode.removeChild( elem );
-}
-
-jQuery.extend = jQuery.fn.extend = function() {
-	// copy reference to target object
-	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
-
-	// Handle a deep copy situation
-	if ( target.constructor == Boolean ) {
-		deep = target;
-		target = arguments[1] || {};
-		// skip the boolean and the target
-		i = 2;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target != "object" && typeof target != "function" )
-		target = {};
-
-	// extend jQuery itself if only one argument is passed
-	if ( length == 1 ) {
-		target = this;
-		i = 0;
-	}
-
-	for ( ; i < length; i++ )
-		// Only deal with non-null/undefined values
-		if ( (options = arguments[ i ]) != null )
-			// Extend the base object
-			for ( var name in options ) {
-				// Prevent never-ending loop
-				if ( target === options[ name ] )
-					continue;
-
-				// Recurse if we're merging object values
-				if ( deep && options[ name ] && typeof options[ name ] == "object" && target[ name ] && !options[ name ].nodeType )
-					target[ name ] = jQuery.extend( target[ name ], options[ name ] );
-
-				// Don't bring in undefined values
-				else if ( options[ name ] != undefined )
-					target[ name ] = options[ name ];
-
-			}
-
-	// Return the modified object
-	return target;
-};
-
-var expando = "jQuery" + (new Date()).getTime(), uuid = 0, windowData = {};
-
-// exclude the following css properties to add px
-var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;
-
-jQuery.extend({
-	noConflict: function( deep ) {
-		window.$ = _$;
-
-		if ( deep )
-			window.jQuery = _jQuery;
-
-		return jQuery;
-	},
-
-	// See test/unit/core.js for details concerning this function.
-	isFunction: function( fn ) {
-		return !!fn && typeof fn != "string" && !fn.nodeName && 
-			fn.constructor != Array && /function/i.test( fn + "" );
-	},
-	
-	// check if an element is in a (or is an) XML document
-	isXMLDoc: function( elem ) {
-		return elem.documentElement && !elem.body ||
-			elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
-	},
-
-	// Evalulates a script in a global context
-	globalEval: function( data ) {
-		data = jQuery.trim( data );
-
-		if ( data ) {
-			// Inspired by code by Andrea Giammarchi
-			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
-			var head = document.getElementsByTagName("head")[0] || document.documentElement,
-				script = document.createElement("script");
-
-			script.type = "text/javascript";
-			if ( jQuery.browser.msie )
-				script.text = data;
-			else
-				script.appendChild( document.createTextNode( data ) );
-
-			head.appendChild( script );
-			head.removeChild( script );
-		}
-	},
-
-	nodeName: function( elem, name ) {
-		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
-	},
-	
-	cache: {},
-	
-	data: function( elem, name, data ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// Compute a unique ID for the element
-		if ( !id ) 
-			id = elem[ expando ] = ++uuid;
-
-		// Only generate the data cache if we're
-		// trying to access or manipulate it
-		if ( name && !jQuery.cache[ id ] )
-			jQuery.cache[ id ] = {};
-		
-		// Prevent overriding the named cache with undefined values
-		if ( data != undefined )
-			jQuery.cache[ id ][ name ] = data;
-		
-		// Return the named cache data, or the ID for the element	
-		return name ?
-			jQuery.cache[ id ][ name ] :
-			id;
-	},
-	
-	removeData: function( elem, name ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// If we want to remove a specific section of the element's data
-		if ( name ) {
-			if ( jQuery.cache[ id ] ) {
-				// Remove the section of cache data
-				delete jQuery.cache[ id ][ name ];
-
-				// If we've removed all the data, remove the element's cache
-				name = "";
-
-				for ( name in jQuery.cache[ id ] )
-					break;
-
-				if ( !name )
-					jQuery.removeData( elem );
-			}
-
-		// Otherwise, we want to remove all of the element's data
-		} else {
-			// Clean up the element expando
-			try {
-				delete elem[ expando ];
-			} catch(e){
-				// IE has trouble directly removing the expando
-				// but it's ok with using removeAttribute
-				if ( elem.removeAttribute )
-					elem.removeAttribute( expando );
-			}
-
-			// Completely remove the data cache
-			delete jQuery.cache[ id ];
-		}
-	},
-
-	// args is for internal usage only
-	each: function( object, callback, args ) {
-		if ( args ) {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.apply( object[ name ], args ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length; i < length; i++ )
-					if ( callback.apply( object[ i ], args ) === false )
-						break;
-
-		// A special, fast, case for the most common use of each
-		} else {
-			if ( object.length == undefined ) {
-				for ( var name in object )
-					if ( callback.call( object[ name ], name, object[ name ] ) === false )
-						break;
-			} else
-				for ( var i = 0, length = object.length, value = object[0]; 
-					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
-		}
-
-		return object;
-	},
-	
-	prop: function( elem, value, type, i, name ) {
-			// Handle executable functions
-			if ( jQuery.isFunction( value ) )
-				value = value.call( elem, i );
-				
-			// Handle passing in a number to a CSS property
-			return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
-				value + "px" :
-				value;
-	},
-
-	className: {
-		// internal only, use addClass("class")
-		add: function( elem, classNames ) {
-			jQuery.each((classNames || "").split(/\s+/), function(i, className){
-				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
-					elem.className += (elem.className ? " " : "") + className;
-			});
-		},
-
-		// internal only, use removeClass("class")
-		remove: function( elem, classNames ) {
-			if (elem.nodeType == 1)
-				elem.className = classNames != undefined ?
-					jQuery.grep(elem.className.split(/\s+/), function(className){
-						return !jQuery.className.has( classNames, className );	
-					}).join(" ") :
-					"";
-		},
-
-		// internal only, use is(".class")
-		has: function( elem, className ) {
-			return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
-		}
-	},
-
-	// A method for quickly swapping in/out CSS properties to get correct calculations
-	swap: function( elem, options, callback ) {
-		var old = {};
-		// Remember the old values, and insert the new ones
-		for ( var name in options ) {
-			old[ name ] = elem.style[ name ];
-			elem.style[ name ] = options[ name ];
-		}
-
-		callback.call( elem );
-
-		// Revert the old values
-		for ( var name in options )
-			elem.style[ name ] = old[ name ];
-	},
-
-	css: function( elem, name, force ) {
-		if ( name == "width" || name == "height" ) {
-			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
-		
-			function getWH() {
-				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
-				var padding = 0, border = 0;
-				jQuery.each( which, function() {
-					padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
-					border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
-				});
-				val -= Math.round(padding + border);
-			}
-		
-			if ( jQuery(elem).is(":visible") )
-				getWH();
-			else
-				jQuery.swap( elem, props, getWH );
-			
-			return Math.max(0, val);
-		}
-		
-		return jQuery.curCSS( elem, name, force );
-	},
-
-	curCSS: function( elem, name, force ) {
-		var ret;
-
-		// A helper method for determining if an element's values are broken
-		function color( elem ) {
-			if ( !jQuery.browser.safari )
-				return false;
-
-			var ret = document.defaultView.getComputedStyle( elem, null );
-			return !ret || ret.getPropertyValue("color") == "";
-		}
-
-		// We need to handle opacity special in IE
-		if ( name == "opacity" && jQuery.browser.msie ) {
-			ret = jQuery.attr( elem.style, "opacity" );
-
-			return ret == "" ?
-				"1" :
-				ret;
-		}
-		// Opera sometimes will give the wrong display answer, this fixes it, see #2037
-		if ( jQuery.browser.opera && name == "display" ) {
-			var save = elem.style.outline;
-			elem.style.outline = "0 solid black";
-			elem.style.outline = save;
-		}
-		
-		// Make sure we're using the right name for getting the float value
-		if ( name.match( /float/i ) )
-			name = styleFloat;
-
-		if ( !force && elem.style && elem.style[ name ] )
-			ret = elem.style[ name ];
-
-		else if ( document.defaultView && document.defaultView.getComputedStyle ) {
-
-			// Only "float" is needed here
-			if ( name.match( /float/i ) )
-				name = "float";
-
-			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
-
-			var getComputedStyle = document.defaultView.getComputedStyle( elem, null );
-
-			if ( getComputedStyle && !color( elem ) )
-				ret = getComputedStyle.getPropertyValue( name );
-
-			// If the element isn't reporting its values properly in Safari
-			// then some display: none elements are involved
-			else {
-				var swap = [], stack = [];
-
-				// Locate all of the parent display: none elements
-				for ( var a = elem; a && color(a); a = a.parentNode )
-					stack.unshift(a);
-
-				// Go through and make them visible, but in reverse
-				// (It would be better if we knew the exact display type that they had)
-				for ( var i = 0; i < stack.length; i++ )
-					if ( color( stack[ i ] ) ) {
-						swap[ i ] = stack[ i ].style.display;
-						stack[ i ].style.display = "block";
-					}
-
-				// Since we flip the display style, we have to handle that
-				// one special, otherwise get the value
-				ret = name == "display" && swap[ stack.length - 1 ] != null ?
-					"none" :
-					( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || "";
-
-				// Finally, revert the display styles back
-				for ( var i = 0; i < swap.length; i++ )
-					if ( swap[ i ] != null )
-						stack[ i ].style.display = swap[ i ];
-			}
-
-			// We should always get a number back from opacity
-			if ( name == "opacity" && ret == "" )
-				ret = "1";
-
-		} else if ( elem.currentStyle ) {
-			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
-
-			// From the awesome hack by Dean Edwards
-			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
-
-			// If we're not dealing with a regular pixel number
-			// but a number that has a weird ending, we need to convert it to pixels
-			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
-				// Remember the original values
-				var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left;
-
-				// Put in the new values to get a computed value out
-				elem.runtimeStyle.left = elem.currentStyle.left;
-				elem.style.left = ret || 0;
-				ret = elem.style.pixelLeft + "px";
-
-				// Revert the changed values
-				elem.style.left = style;
-				elem.runtimeStyle.left = runtimeStyle;
-			}
-		}
-
-		return ret;
-	},
-	
-	clean: function( elems, context ) {
-		var ret = [];
-		context = context || document;
-		// !context.createElement fails in IE with an error but returns typeof 'object'
-		if (typeof context.createElement == 'undefined') 
-			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
-
-		jQuery.each(elems, function(i, elem){
-			if ( !elem )
-				return;
-
-			if ( elem.constructor == Number )
-				elem = elem.toString();
-			
-			// Convert html string into DOM nodes
-			if ( typeof elem == "string" ) {
-				// Fix "XHTML"-style tags in all browsers
-				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
-					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
-						all :
-						front + "></" + tag + ">";
-				});
-
-				// Trim whitespace, otherwise indexOf won't work as expected
-				var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
-
-				var wrap =
-					// option or optgroup
-					!tags.indexOf("<opt") &&
-					[ 1, "<select multiple='multiple'>", "</select>" ] ||
-					
-					!tags.indexOf("<leg") &&
-					[ 1, "<fieldset>", "</fieldset>" ] ||
-					
-					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
-					[ 1, "<table>", "</table>" ] ||
-					
-					!tags.indexOf("<tr") &&
-					[ 2, "<table><tbody>", "</tbody></table>" ] ||
-					
-				 	// <thead> matched above
-					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
-					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
-					
-					!tags.indexOf("<col") &&
-					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
-
-					// IE can't serialize <link> and <script> tags normally
-					jQuery.browser.msie &&
-					[ 1, "div<div>", "</div>" ] ||
-					
-					[ 0, "", "" ];
-
-				// Go to html and back, then peel off extra wrappers
-				div.innerHTML = wrap[1] + elem + wrap[2];
-				
-				// Move to the right depth
-				while ( wrap[0]-- )
-					div = div.lastChild;
-				
-				// Remove IE's autoinserted <tbody> from table fragments
-				if ( jQuery.browser.msie ) {
-					
-					// String was a <table>, *may* have spurious <tbody>
-					var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
-						div.firstChild && div.firstChild.childNodes :
-						
-						// String was a bare <thead> or <tfoot>
-						wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
-							div.childNodes :
-							[];
-				
-					for ( var j = tbody.length - 1; j >= 0 ; --j )
-						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
-							tbody[ j ].parentNode.removeChild( tbody[ j ] );
-					
-					// IE completely kills leading whitespace when innerHTML is used	
-					if ( /^\s/.test( elem ) )	
-						div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
-				
-				}
-				
-				elem = jQuery.makeArray( div.childNodes );
-			}
-
-			if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
-				return;
-
-			if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
-				ret.push( elem );
-
-			else
-				ret = jQuery.merge( ret, elem );
-
-		});
-
-		return ret;
-	},
-	
-	attr: function( elem, name, value ) {
-		// don't set attributes on text and comment nodes
-		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
-			return undefined;
-
-		var fix = jQuery.isXMLDoc( elem ) ?
-			{} :
-			jQuery.props;
-
-		// Safari mis-reports the default selected property of a hidden option
-		// Accessing the parent's selectedIndex property fixes it
-		if ( name == "selected" && jQuery.browser.safari )
-			elem.parentNode.selectedIndex;
-		
-		// Certain attributes only work when accessed via the old DOM 0 way
-		if ( fix[ name ] ) {
-			if ( value != undefined )
-				elem[ fix[ name ] ] = value;
-
-			return elem[ fix[ name ] ];
-
-		} else if ( jQuery.browser.msie && name == "style" )
-			return jQuery.attr( elem.style, "cssText", value );
-
-		else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName( elem, "form" ) && (name == "action" || name == "method") )
-			return elem.getAttributeNode( name ).nodeValue;
-
-		// IE elem.getAttribute passes even for style
-		else if ( elem.tagName ) {
-
-			if ( value != undefined ) {
-				// We can't allow the type property to be changed (since it causes problems in IE)
-				if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
-					throw "type property can't be changed";
-
-				// convert the value to a string (all browsers do this but IE) see #1070
-				elem.setAttribute( name, "" + value );
-			}
-
-			if ( jQuery.browser.msie && /href|src/.test( name ) && !jQuery.isXMLDoc( elem ) ) 
-				return elem.getAttribute( name, 2 );
-
-			return elem.getAttribute( name );
-
-		// elem is actually elem.style ... set the style
-		} else {
-			// IE actually uses filters for opacity
-			if ( name == "opacity" && jQuery.browser.msie ) {
-				if ( value != undefined ) {
-					// IE has trouble with opacity if it does not have layout
-					// Force it by setting the zoom level
-					elem.zoom = 1; 
-	
-					// Set the alpha filter to set the opacity
-					elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
-						(parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
-				}
-	
-				return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
-					(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() :
-					"";
-			}
-
-			name = name.replace(/-([a-z])/ig, function(all, letter){
-				return letter.toUpperCase();
-			});
-
-			if ( value != undefined )
-				elem[ name ] = value;
-
-			return elem[ name ];
-		}
-	},
-	
-	trim: function( text ) {
-		return (text || "").replace( /^\s+|\s+$/g, "" );
-	},
-
-	makeArray: function( array ) {
-		var ret = [];
-
-		// Need to use typeof to fight Safari childNodes crashes
-		if ( typeof array != "array" )
-			for ( var i = 0, length = array.length; i < length; i++ )
-				ret.push( array[ i ] );
-		else
-			ret = array.slice( 0 );
-
-		return ret;
-	},
-
-	inArray: function( elem, array ) {
-		for ( var i = 0, length = array.length; i < length; i++ )
-			if ( array[ i ] == elem )
-				return i;
-
-		return -1;
-	},
-
-	merge: function( first, second ) {
-		// We have to loop this way because IE & Opera overwrite the length
-		// expando of getElementsByTagName
-
-		// Also, we need to make sure that the correct elements are being returned
-		// (IE returns comment nodes in a '*' query)
-		if ( jQuery.browser.msie ) {
-			for ( var i = 0; second[ i ]; i++ )
-				if ( second[ i ].nodeType != 8 )
-					first.push( second[ i ] );
-
-		} else
-			for ( var i = 0; second[ i ]; i++ )
-				first.push( second[ i ] );
-
-		return first;
-	},
-
-	unique: function( array ) {
-		var ret = [], done = {};
-
-		try {
-
-			for ( var i = 0, length = array.length; i < length; i++ ) {
-				var id = jQuery.data( array[ i ] );
-
-				if ( !done[ id ] ) {
-					done[ id ] = true;
-					ret.push( array[ i ] );
-				}
-			}
-
-		} catch( e ) {
-			ret = array;
-		}
-
-		return ret;
-	},
-
-	grep: function( elems, callback, inv ) {
-		var ret = [];
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( var i = 0, length = elems.length; i < length; i++ )
-			if ( !inv && callback( elems[ i ], i ) || inv && !callback( elems[ i ], i ) )
-				ret.push( elems[ i ] );
-
-		return ret;
-	},
-
-	map: function( elems, callback ) {
-		var ret = [];
-
-		// Go through the array, translating each of the items to their
-		// new value (or values).
-		for ( var i = 0, length = elems.length; i < length; i++ ) {
-			var value = callback( elems[ i ], i );
-
-			if ( value !== null && value != undefined ) {
-				if ( value.constructor != Array )
-					value = [ value ];
-
-				ret = ret.concat( value );
-			}
-		}
-
-		return ret;
-	}
-});
-
-var userAgent = navigator.userAgent.toLowerCase();
-
-// Figure out what browser is being used
-jQuery.browser = {
-	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
-	safari: /webkit/.test( userAgent ),
-	opera: /opera/.test( userAgent ),
-	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
-	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
-};
-
-var styleFloat = jQuery.browser.msie ?
-	"styleFloat" :
-	"cssFloat";
-	
-jQuery.extend({
-	// Check to see if the W3C box model is being used
-	boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
-	
-	props: {
-		"for": "htmlFor",
-		"class": "className",
-		"float": styleFloat,
-		cssFloat: styleFloat,
-		styleFloat: styleFloat,
-		innerHTML: "innerHTML",
-		className: "className",
-		value: "value",
-		disabled: "disabled",
-		checked: "checked",
-		readonly: "readOnly",
-		selected: "selected",
-		maxlength: "maxLength",
-		selectedIndex: "selectedIndex",
-		defaultValue: "defaultValue",
-		tagName: "tagName",
-		nodeName: "nodeName"
-	}
-});
-
-jQuery.each({
-	parent: function(elem){return elem.parentNode;},
-	parents: function(elem){return jQuery.dir(elem,"parentNode");},
-	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
-	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
-	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
-	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
-	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
-	children: function(elem){return jQuery.sibling(elem.firstChild);},
-	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
-}, function(name, fn){
-	jQuery.fn[ name ] = function( selector ) {
-		var ret = jQuery.map( this, fn );
-
-		if ( selector && typeof selector == "string" )
-			ret = jQuery.multiFilter( selector, ret );
-
-		return this.pushStack( jQuery.unique( ret ) );
-	};
-});
-
-jQuery.each({
-	appendTo: "append",
-	prependTo: "prepend",
-	insertBefore: "before",
-	insertAfter: "after",
-	replaceAll: "replaceWith"
-}, function(name, original){
-	jQuery.fn[ name ] = function() {
-		var args = arguments;
-
-		return this.each(function(){
-			for ( var i = 0, length = args.length; i < length; i++ )
-				jQuery( args[ i ] )[ original ]( this );
-		});
-	};
-});
-
-jQuery.each({
-	removeAttr: function( name ) {
-		jQuery.attr( this, name, "" );
-		if (this.nodeType == 1) 
-			this.removeAttribute( name );
-	},
-
-	addClass: function( classNames ) {
-		jQuery.className.add( this, classNames );
-	},
-
-	removeClass: function( classNames ) {
-		jQuery.className.remove( this, classNames );
-	},
-
-	toggleClass: function( classNames ) {
-		jQuery.className[ jQuery.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
-	},
-
-	remove: function( selector ) {
-		if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
-			// Prevent memory leaks
-			jQuery( "*", this ).add(this).each(function(){
-				jQuery.event.remove(this);
-				jQuery.removeData(this);
-			});
-			if (this.parentNode)
-				this.parentNode.removeChild( this );
-		}
-	},
-
-	empty: function() {
-		// Remove element nodes and prevent memory leaks
-		jQuery( ">*", this ).remove();
-		
-		// Remove any remaining nodes
-		while ( this.firstChild )
-			this.removeChild( this.firstChild );
-	}
-}, function(name, fn){
-	jQuery.fn[ name ] = function(){
-		return this.each( fn, arguments );
-	};
-});
-
-jQuery.each([ "Height", "Width" ], function(i, name){
-	var type = name.toLowerCase();
-	
-	jQuery.fn[ type ] = function( size ) {
-		// Get window width or height
-		return this[0] == window ?
-			// Opera reports document.body.client[Width/Height] properly in both quirks and standards
-			jQuery.browser.opera && document.body[ "client" + name ] || 
-			
-			// Safari reports inner[Width/Height] just fine (Mozilla and Opera include scroll bar widths)
-			jQuery.browser.safari && window[ "inner" + name ] ||
-			
-			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
-			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || document.body[ "client" + name ] :
-		
-			// Get document width or height
-			this[0] == document ?
-				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
-				Math.max( 
-					Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), 
-					Math.max(document.body["offset" + name], document.documentElement["offset" + name]) 
-				) :
-
-				// Get or set width or height on the element
-				size == undefined ?
-					// Get width or height on the element
-					(this.length ? jQuery.css( this[0], type ) : null) :
-
-					// Set the width or height on the element (default to pixels if value is unitless)
-					this.css( type, size.constructor == String ? size : size + "px" );
-	};
-});
-
-var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417 ?
-		"(?:[\\w*_-]|\\\\.)" :
-		"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
-	quickChild = new RegExp("^>\\s*(" + chars + "+)"),
-	quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),
-	quickClass = new RegExp("^([#.]?)(" + chars + "*)");
-
-jQuery.extend({
-	expr: {
-		"": function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},
-		"#": function(a,i,m){return a.getAttribute("id")==m[2];},
-		":": {
-			// Position Checks
-			lt: function(a,i,m){return i<m[3]-0;},
-			gt: function(a,i,m){return i>m[3]-0;},
-			nth: function(a,i,m){return m[3]-0==i;},
-			eq: function(a,i,m){return m[3]-0==i;},
-			first: function(a,i){return i==0;},
-			last: function(a,i,m,r){return i==r.length-1;},
-			even: function(a,i){return i%2==0;},
-			odd: function(a,i){return i%2;},
-
-			// Child Checks
-			"first-child": function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},
-			"last-child": function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},
-			"only-child": function(a){return !jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},
-
-			// Parent Checks
-			parent: function(a){return a.firstChild;},
-			empty: function(a){return !a.firstChild;},
-
-			// Text Check
-			contains: function(a,i,m){return (a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},
-
-			// Visibility
-			visible: function(a){return "hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},
-			hidden: function(a){return "hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},
-
-			// Form attributes
-			enabled: function(a){return !a.disabled;},
-			disabled: function(a){return a.disabled;},
-			checked: function(a){return a.checked;},
-			selected: function(a){return a.selected||jQuery.attr(a,"selected");},
-
-			// Form elements
-			text: function(a){return "text"==a.type;},
-			radio: function(a){return "radio"==a.type;},
-			checkbox: function(a){return "checkbox"==a.type;},
-			file: function(a){return "file"==a.type;},
-			password: function(a){return "password"==a.type;},
-			submit: function(a){return "submit"==a.type;},
-			image: function(a){return "image"==a.type;},
-			reset: function(a){return "reset"==a.type;},
-			button: function(a){return "button"==a.type||jQuery.nodeName(a,"button");},
-			input: function(a){return /input|select|textarea|button/i.test(a.nodeName);},
-
-			// :has()
-			has: function(a,i,m){return jQuery.find(m[3],a).length;},
-
-			// :header
-			header: function(a){return /h\d/i.test(a.nodeName);},
-
-			// :animated
-			animated: function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}
-		}
-	},
-	
-	// The regular expressions that power the parsing engine
-	parse: [
-		// Match: [@value='test'], [@foo]
-		/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
-
-		// Match: :contains('foo')
-		/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
-
-		// Match: :even, :last-chlid, #id, .class
-		new RegExp("^([:.#]*)(" + chars + "+)")
-	],
-
-	multiFilter: function( expr, elems, not ) {
-		var old, cur = [];
-
-		while ( expr && expr != old ) {
-			old = expr;
-			var f = jQuery.filter( expr, elems, not );
-			expr = f.t.replace(/^\s*,\s*/, "" );
-			cur = not ? elems = f.r : jQuery.merge( cur, f.r );
-		}
-
-		return cur;
-	},
-
-	find: function( t, context ) {
-		// Quickly handle non-string expressions
-		if ( typeof t != "string" )
-			return [ t ];
-
-		// check to make sure context is a DOM element or a document
-		if ( context && context.nodeType != 1 && context.nodeType != 9)
-			return [ ];
-
-		// Set the correct context (if none is provided)
-		context = context || document;
-
-		// Initialize the search
-		var ret = [context], done = [], last, nodeName;
-
-		// Continue while a selector expression exists, and while
-		// we're no longer looping upon ourselves
-		while ( t && last != t ) {
-			var r = [];
-			last = t;
-
-			t = jQuery.trim(t);
-
-			var foundToken = false;
-
-			// An attempt at speeding up child selectors that
-			// point to a specific element tag
-			var re = quickChild;
-			var m = re.exec(t);
-
-			if ( m ) {
-				nodeName = m[1].toUpperCase();
-
-				// Perform our own iteration and filter
-				for ( var i = 0; ret[i]; i++ )
-					for ( var c = ret[i].firstChild; c; c = c.nextSibling )
-						if ( c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName) )
-							r.push( c );
-
-				ret = r;
-				t = t.replace( re, "" );
-				if ( t.indexOf(" ") == 0 ) continue;
-				foundToken = true;
-			} else {
-				re = /^([>+~])\s*(\w*)/i;
-
-				if ( (m = re.exec(t)) != null ) {
-					r = [];
-
-					var merge = {};
-					nodeName = m[2].toUpperCase();
-					m = m[1];
-
-					for ( var j = 0, rl = ret.length; j < rl; j++ ) {
-						var n = m == "~" || m == "+" ? ret[j].nextSibling : ret[j].firstChild;
-						for ( ; n; n = n.nextSibling )
-							if ( n.nodeType == 1 ) {
-								var id = jQuery.data(n);
-
-								if ( m == "~" && merge[id] ) break;
-								
-								if (!nodeName || n.nodeName.toUpperCase() == nodeName ) {
-									if ( m == "~" ) merge[id] = true;
-									r.push( n );
-								}
-								
-								if ( m == "+" ) break;
-							}
-					}
-
-					ret = r;
-
-					// And remove the token
-					t = jQuery.trim( t.replace( re, "" ) );
-					foundToken = true;
-				}
-			}
-
-			// See if there's still an expression, and that we haven't already
-			// matched a token
-			if ( t && !foundToken ) {
-				// Handle multiple expressions
-				if ( !t.indexOf(",") ) {
-					// Clean the result set
-					if ( context == ret[0] ) ret.shift();
-
-					// Merge the result sets
-					done = jQuery.merge( done, ret );
-
-					// Reset the context
-					r = ret = [context];
-
-					// Touch up the selector string
-					t = " " + t.substr(1,t.length);
-
-				} else {
-					// Optimize for the case nodeName#idName
-					var re2 = quickID;
-					var m = re2.exec(t);
-					
-					// Re-organize the results, so that they're consistent
-					if ( m ) {
-						m = [ 0, m[2], m[3], m[1] ];
-
-					} else {
-						// Otherwise, do a traditional filter check for
-						// ID, class, and element selectors
-						re2 = quickClass;
-						m = re2.exec(t);
-					}
-
-					m[2] = m[2].replace(/\\/g, "");
-
-					var elem = ret[ret.length-1];
-
-					// Try to do a global search by ID, where we can
-					if ( m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem) ) {
-						// Optimization for HTML document case
-						var oid = elem.getElementById(m[2]);
-						
-						// Do a quick check for the existence of the actual ID attribute
-						// to avoid selecting by the name attribute in IE
-						// also check to insure id is a string to avoid selecting an element with the name of 'id' inside a form
-						if ( (jQuery.browser.msie||jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2] )
-							oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
-
-						// Do a quick check for node name (where applicable) so
-						// that div#foo searches will be really fast
-						ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
-					} else {
-						// We need to find all descendant elements
-						for ( var i = 0; ret[i]; i++ ) {
-							// Grab the tag name being searched for
-							var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*" : m[2];
-
-							// Handle IE7 being really dumb about <object>s
-							if ( tag == "*" && ret[i].nodeName.toLowerCase() == "object" )
-								tag = "param";
-
-							r = jQuery.merge( r, ret[i].getElementsByTagName( tag ));
-						}
-
-						// It's faster to filter by class and be done with it
-						if ( m[1] == "." )
-							r = jQuery.classFilter( r, m[2] );
-
-						// Same with ID filtering
-						if ( m[1] == "#" ) {
-							var tmp = [];
-
-							// Try to find the element with the ID
-							for ( var i = 0; r[i]; i++ )
-								if ( r[i].getAttribute("id") == m[2] ) {
-									tmp = [ r[i] ];
-									break;
-								}
-
-							r = tmp;
-						}
-
-						ret = r;
-					}
-
-					t = t.replace( re2, "" );
-				}
-
-			}
-
-			// If a selector string still exists
-			if ( t ) {
-				// Attempt to filter it
-				var val = jQuery.filter(t,r);
-				ret = r = val.r;
-				t = jQuery.trim(val.t);
-			}
-		}
-
-		// An error occurred with the selector;
-		// just return an empty set instead
-		if ( t )
-			ret = [];
-
-		// Remove the root context
-		if ( ret && context == ret[0] )
-			ret.shift();
-
-		// And combine the results
-		done = jQuery.merge( done, ret );
-
-		return done;
-	},
-
-	classFilter: function(r,m,not){
-		m = " " + m + " ";
-		var tmp = [];
-		for ( var i = 0; r[i]; i++ ) {
-			var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
-			if ( !not && pass || not && !pass )
-				tmp.push( r[i] );
-		}
-		return tmp;
-	},
-
-	filter: function(t,r,not) {
-		var last;
-
-		// Look for common filter expressions
-		while ( t && t != last ) {
-			last = t;
-
-			var p = jQuery.parse, m;
-
-			for ( var i = 0; p[i]; i++ ) {
-				m = p[i].exec( t );
-
-				if ( m ) {
-					// Remove what we just matched
-					t = t.substring( m[0].length );
-
-					m[2] = m[2].replace(/\\/g, "");
-					break;
-				}
-			}
-
-			if ( !m )
-				break;
-
-			// :not() is a special case that can be optimized by
-			// keeping it out of the expression list
-			if ( m[1] == ":" && m[2] == "not" )
-				// optimize if only one selector found (most common case)
-				r = isSimple.test( m[3] ) ?
-					jQuery.filter(m[3], r, true).r :
-					jQuery( r ).not( m[3] );
-
-			// We can get a big speed boost by filtering by class here
-			else if ( m[1] == "." )
-				r = jQuery.classFilter(r, m[2], not);
-
-			else if ( m[1] == "[" ) {
-				var tmp = [], type = m[3];
-				
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var a = r[i], z = a[ jQuery.props[m[2]] || m[2] ];
-					
-					if ( z == null || /href|src|selected/.test(m[2]) )
-						z = jQuery.attr(a,m[2]) || '';
-
-					if ( (type == "" && !!z ||
-						 type == "=" && z == m[5] ||
-						 type == "!=" && z != m[5] ||
-						 type == "^=" && z && !z.indexOf(m[5]) ||
-						 type == "$=" && z.substr(z.length - m[5].length) == m[5] ||
-						 (type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0) ^ not )
-							tmp.push( a );
-				}
-				
-				r = tmp;
-
-			// We can get a speed boost by handling nth-child here
-			} else if ( m[1] == ":" && m[2] == "nth-child" ) {
-				var merge = {}, tmp = [],
-					// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
-					test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
-						m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||
-						!/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
-					// calculate the numbers (first)n+(last) including if they are negative
-					first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
- 
-				// loop through all the elements left in the jQuery object
-				for ( var i = 0, rl = r.length; i < rl; i++ ) {
-					var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
-
-					if ( !merge[id] ) {
-						var c = 1;
-
-						for ( var n = parentNode.firstChild; n; n = n.nextSibling )
-							if ( n.nodeType == 1 )
-								n.nodeIndex = c++;
-
-						merge[id] = true;
-					}
-
-					var add = false;
-
-					if ( first == 0 ) {
-						if ( node.nodeIndex == last )
-							add = true;
-					} else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
-						add = true;
-
-					if ( add ^ not )
-						tmp.push( node );
-				}
-
-				r = tmp;
-
-			// Otherwise, find the expression to execute
-			} else {
-				var fn = jQuery.expr[ m[1] ];
-				if ( typeof fn == "object" )
-					fn = fn[ m[2] ];
-
-				if ( typeof fn == "string" )
-					fn = eval("false||function(a,i){return " + fn + ";}");
-
-				// Execute it against the current filter
-				r = jQuery.grep( r, function(elem, i){
-					return fn(elem, i, m, r);
-				}, not );
-			}
-		}
-
-		// Return an array of filtered elements (r)
-		// and the modified expression string (t)
-		return { r: r, t: t };
-	},
-
-	dir: function( elem, dir ){
-		var matched = [];
-		var cur = elem[dir];
-		while ( cur && cur != document ) {
-			if ( cur.nodeType == 1 )
-				matched.push( cur );
-			cur = cur[dir];
-		}
-		return matched;
-	},
-	
-	nth: function(cur,result,dir,elem){
-		result = result || 1;
-		var num = 0;
-
-		for ( ; cur; cur = cur[dir] )
-			if ( cur.nodeType == 1 && ++num == result )
-				break;
-
-		return cur;
-	},
-	
-	sibling: function( n, elem ) {
-		var r = [];
-
-		for ( ; n; n = n.nextSibling ) {
-			if ( n.nodeType == 1 && (!elem || n != elem) )
-				r.push( n );
-		}
-
-		return r;
-	}
-});
-
-/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code orignated from 
- * Dean Edwards' addEvent library.
- */
-jQuery.event = {
-
-	// Bind an event to an element
-	// Original by Dean Edwards
-	add: function(elem, types, handler, data) {
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		// For whatever reason, IE has trouble passing the window object
-		// around, causing it to be cloned in the process
-		if ( jQuery.browser.msie && elem.setInterval != undefined )
-			elem = window;
-
-		// Make sure that the function being executed has a unique ID
-		if ( !handler.guid )
-			handler.guid = this.guid++;
-			
-		// if data is passed, bind to handler 
-		if( data != undefined ) { 
-			// Create temporary function pointer to original handler 
-			var fn = handler; 
-
-			// Create unique handler function, wrapped around original handler 
-			handler = function() { 
-				// Pass arguments and context to original handler 
-				return fn.apply(this, arguments); 
-			};
-
-			// Store data in unique handler 
-			handler.data = data;
-
-			// Set the guid of unique handler to the same of original handler, so it can be removed 
-			handler.guid = fn.guid;
-		}
-
-		// Init the element's event structure
-		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
-			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
-				// returned undefined or false
-				var val;
-
-				// Handle the second event of a trigger and when
-				// an event is called after a page has unloaded
-				if ( typeof jQuery == "undefined" || jQuery.event.triggered )
-					return val;
-		
-				val = jQuery.event.handle.apply(arguments.callee.elem, arguments);
-		
-				return val;
-			});
-		// Add elem as a property of the handle function
-		// This is to prevent a memory leak with non-native
-		// event in IE.
-		handle.elem = elem;
-			
-			// Handle multiple events seperated by a space
-			// jQuery(...).bind("mouseover mouseout", fn);
-			jQuery.each(types.split(/\s+/), function(index, type) {
-				// Namespaced event handlers
-				var parts = type.split(".");
-				type = parts[0];
-				handler.type = parts[1];
-
-				// Get the current list of functions bound to this event
-				var handlers = events[type];
-
-				// Init the event handler queue
-				if (!handlers) {
-					handlers = events[type] = {};
-		
-					// Check for a special event handler
-					// Only use addEventListener/attachEvent if the special
-					// events handler returns false
-					if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) {
-						// Bind the global event handler to the element
-						if (elem.addEventListener)
-							elem.addEventListener(type, handle, false);
-						else if (elem.attachEvent)
-							elem.attachEvent("on" + type, handle);
-					}
-				}
-
-				// Add the function to the element's handler list
-				handlers[handler.guid] = handler;
-
-				// Keep track of which events have been used, for global triggering
-				jQuery.event.global[type] = true;
-			});
-		
-		// Nullify elem to prevent memory leaks in IE
-		elem = null;
-	},
-
-	guid: 1,
-	global: {},
-
-	// Detach an event or set of events from an element
-	remove: function(elem, types, handler) {
-		// don't do events on text and comment nodes
-		if ( elem.nodeType == 3 || elem.nodeType == 8 )
-			return;
-
-		var events = jQuery.data(elem, "events"), ret, index;
-
-		if ( events ) {
-			// Unbind all events for the element
-			if ( types == undefined || (typeof types == "string" && types.charAt(0) == ".") )
-				for ( var type in events )
-					this.remove( elem, type + (types || "") );
-			else {
-				// types is actually an event object here
-				if ( types.type ) {
-					handler = types.handler;
-					types = types.type;
-				}
-				
-				// Handle multiple events seperated by a space
-				// jQuery(...).unbind("mouseover mouseout", fn);
-				jQuery.each(types.split(/\s+/), function(index, type){
-					// Namespaced event handlers
-					var parts = type.split(".");
-					type = parts[0];
-					
-					if ( events[type] ) {
-						// remove the given handler for the given type
-						if ( handler )
-							delete events[type][handler.guid];
-			
-						// remove all handlers for the given type
-						else
-							for ( handler in events[type] )
-								// Handle the removal of namespaced events
-								if ( !parts[1] || events[type][handler].type == parts[1] )
-									delete events[type][handler];
-
-						// remove generic event handler if no more handlers exist
-						for ( ret in events[type] ) break;
-						if ( !ret ) {
-							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) {
-								if (elem.removeEventListener)
-									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
-								else if (elem.detachEvent)
-									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
-							}
-							ret = null;
-							delete events[type];
-						}
-					}
-				});
-			}
-
-			// Remove the expando if it's no longer used
-			for ( ret in events ) break;
-			if ( !ret ) {
-				var handle = jQuery.data( elem, "handle" );
-				if ( handle ) handle.elem = null;
-				jQuery.removeData( elem, "events" );
-				jQuery.removeData( elem, "handle" );
-			}
-		}
-	},
-
-	trigger: function(type, data, elem, donative, extra) {
-		// Clone the incoming data, if any
-		data = jQuery.makeArray(data || []);
-
-		if ( type.indexOf("!") >= 0 ) {
-			type = type.slice(0, -1);
-			var exclusive = true;
-		}
-
-		// Handle a global trigger
-		if ( !elem ) {
-			// Only trigger if we've ever bound an event for it
-			if ( this.global[type] )
-				jQuery("*").add([window, document]).trigger(type, data);
-
-		// Handle triggering a single element
-		} else {
-			// don't do events on text and comment nodes
-			if ( elem.nodeType == 3 || elem.nodeType == 8 )
-				return undefined;
-
-			var val, ret, fn = jQuery.isFunction( elem[ type ] || null ),
-				// Check to see if we need to provide a fake event, or not
-				event = !data[0] || !data[0].preventDefault;
-			
-			// Pass along a fake event
-			if ( event )
-				data.unshift( this.fix({ type: type, target: elem }) );
-
-			// Enforce the right trigger type
-			data[0].type = type;
-			if ( exclusive )
-				data[0].exclusive = true;
-
-			// Trigger the event
-			if ( jQuery.isFunction( jQuery.data(elem, "handle") ) )
-				val = jQuery.data(elem, "handle").apply( elem, data );
-
-			// Handle triggering native .onfoo handlers
-			if ( !fn && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
-				val = false;
-
-			// Extra functions don't get the custom event object
-			if ( event )
-				data.shift();
-
-			// Handle triggering of extra function
-			if ( extra && jQuery.isFunction( extra ) ) {
-				// call the extra function and tack the current return value on the end for possible inspection
-				ret = extra.apply( elem, val == null ? data : data.concat( val ) );
-				// if anything is returned, give it precedence and have it overwrite the previous value
-				if (ret !== undefined)
-					val = ret;
-			}
-
-			// Trigger the native events (except for clicks on links)
-			if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
-				this.triggered = true;
-				try {
-					elem[ type ]();
-				// prevent IE from throwing an error for some hidden elements
-				} catch (e) {}
-			}
-
-			this.triggered = false;
-		}
-
-		return val;
-	},
-
-	handle: function(event) {
-		// returned undefined or false
-		var val;
-
-		// Empty object is for triggered events with no data
-		event = jQuery.event.fix( event || window.event || {} ); 
-
-		// Namespaced event handlers
-		var parts = event.type.split(".");
-		event.type = parts[0];
-
-		var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
-		args.unshift( event );
-
-		for ( var j in handlers ) {
-			var handler = handlers[j];
-			// Pass in a reference to the handler function itself
-			// So that we can later remove it
-			args[0].handler = handler;
-			args[0].data = handler.data;
-
-			// Filter the functions by class
-			if ( !parts[1] && !event.exclusive || handler.type == parts[1] ) {
-				var ret = handler.apply( this, args );
-
-				if ( val !== false )
-					val = ret;
-
-				if ( ret === false ) {
-					event.preventDefault();
-					event.stopPropagation();
-				}
-			}
-		}
-
-		// Clean up added properties in IE to prevent memory leak
-		if (jQuery.browser.msie)
-			event.target = event.preventDefault = event.stopPropagation =
-				event.handler = event.data = null;
-
-		return val;
-	},
-
-	fix: function(event) {
-		// store a copy of the original event object 
-		// and clone to set read-only properties
-		var originalEvent = event;
-		event = jQuery.extend({}, originalEvent);
-		
-		// add preventDefault and stopPropagation since 
-		// they will not work on the clone
-		event.preventDefault = function() {
-			// if preventDefault exists run it on the original event
-			if (originalEvent.preventDefault)
-				originalEvent.preventDefault();
-			// otherwise set the returnValue property of the original event to false (IE)
-			originalEvent.returnValue = false;
-		};
-		event.stopPropagation = function() {
-			// if stopPropagation exists run it on the original event
-			if (originalEvent.stopPropagation)
-				originalEvent.stopPropagation();
-			// otherwise set the cancelBubble property of the original event to true (IE)
-			originalEvent.cancelBubble = true;
-		};
-		
-		// Fix target property, if necessary
-		if ( !event.target )
-			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
-				
-		// check if target is a textnode (safari)
-		if ( event.target.nodeType == 3 )
-			event.target = originalEvent.target.parentNode;
-
-		// Add relatedTarget, if necessary
-		if ( !event.relatedTarget && event.fromElement )
-			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
-
-		// Calculate pageX/Y if missing and clientX/Y available
-		if ( event.pageX == null && event.clientX != null ) {
-			var doc = document.documentElement, body = document.body;
-			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
-			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
-		}
-			
-		// Add which for key events
-		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
-			event.which = event.charCode || event.keyCode;
-		
-		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
-		if ( !event.metaKey && event.ctrlKey )
-			event.metaKey = event.ctrlKey;
-
-		// Add which for click: 1 == left; 2 == middle; 3 == right
-		// Note: button is not normalized, so don't use it
-		if ( !event.which && event.button )
-			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
-			
-		return event;
-	},
-	
-	special: {
-		ready: {
-			setup: function() {
-				// Make sure the ready event is setup
-				bindReady();
-				return;
-			},
-			
-			teardown: function() { return; }
-		},
-		
-		mouseenter: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseenter
-				arguments[0].type = "mouseenter";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		},
-	
-		mouseleave: {
-			setup: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-		
-			teardown: function() {
-				if ( jQuery.browser.msie ) return false;
-				jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
-				return true;
-			},
-			
-			handler: function(event) {
-				// If we actually just moused on to a sub-element, ignore it
-				if ( withinElement(event, this) ) return true;
-				// Execute the right handlers by setting the event type to mouseleave
-				arguments[0].type = "mouseleave";
-				return jQuery.event.handle.apply(this, arguments);
-			}
-		}
-	}
-};
-
-jQuery.fn.extend({
-	bind: function( type, data, fn ) {
-		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
-			jQuery.event.add( this, type, fn || data, fn && data );
-		});
-	},
-	
-	one: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.add( this, type, function(event) {
-				jQuery(this).unbind(event);
-				return (fn || data).apply( this, arguments);
-			}, fn && data);
-		});
-	},
-
-	unbind: function( type, fn ) {
-		return this.each(function(){
-			jQuery.event.remove( this, type, fn );
-		});
-	},
-
-	trigger: function( type, data, fn ) {
-		return this.each(function(){
-			jQuery.event.trigger( type, data, this, true, fn );
-		});
-	},
-
-	triggerHandler: function( type, data, fn ) {
-		if ( this[0] )
-			return jQuery.event.trigger( type, data, this[0], false, fn );
-		return undefined;
-	},
-
-	toggle: function() {
-		// Save reference to arguments for access in closure
-		var args = arguments;
-
-		return this.click(function(event) {
-			// Figure out which function to execute
-			this.lastToggle = 0 == this.lastToggle ? 1 : 0;
-			
-			// Make sure that clicks stop
-			event.preventDefault();
-			
-			// and execute the function
-			return args[this.lastToggle].apply( this, arguments ) || false;
-		});
-	},
-
-	hover: function(fnOver, fnOut) {
-		return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
-	},
-	
-	ready: function(fn) {
-		// Attach the listeners
-		bindReady();
-
-		// If the DOM is already ready
-		if ( jQuery.isReady )
-			// Execute the function immediately
-			fn.call( document, jQuery );
-			
-		// Otherwise, remember the function for later
-		else
-			// Add the function to the wait list
-			jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
-	
-		return this;
-	}
-});
-
-jQuery.extend({
-	isReady: false,
-	readyList: [],
-	// Handle when the DOM is ready
-	ready: function() {
-		// Make sure that the DOM is not already loaded
-		if ( !jQuery.isReady ) {
-			// Remember that the DOM is ready
-			jQuery.isReady = true;
-			
-			// If there are functions bound, to execute
-			if ( jQuery.readyList ) {
-				// Execute all of them
-				jQuery.each( jQuery.readyList, function(){
-					this.apply( document );
-				});
-				
-				// Reset the list of functions
-				jQuery.readyList = null;
-			}
-		
-			// Trigger any bound ready events
-			jQuery(document).triggerHandler("ready");
-		}
-	}
-});
-
-var readyBound = false;
-
-function bindReady(){
-	if ( readyBound ) return;
-	readyBound = true;
-
-	// Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
-	if ( document.addEventListener && !jQuery.browser.opera)
-		// Use the handy event callback
-		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
-	
-	// If IE is used and is not in a frame
-	// Continually check to see if the document is ready
-	if ( jQuery.browser.msie && window == top ) (function(){
-		if (jQuery.isReady) return;
-		try {
-			// If IE is used, use the trick by Diego Perini
-			// http://javascript.nwbox.com/IEContentLoaded/
-			document.documentElement.doScroll("left");
-		} catch( error ) {
-			setTimeout( arguments.callee, 0 );
-			return;
-		}
-		// and execute any waiting functions
-		jQuery.ready();
-	})();
-
-	if ( jQuery.browser.opera )
-		document.addEventListener( "DOMContentLoaded", function () {
-			if (jQuery.isReady) return;
-			for (var i = 0; i < document.styleSheets.length; i++)
-				if (document.styleSheets[i].disabled) {
-					setTimeout( arguments.callee, 0 );
-					return;
-				}
-			// and execute any waiting functions
-			jQuery.ready();
-		}, false);
-
-	if ( jQuery.browser.safari ) {
-		var numStyles;
-		(function(){
-			if (jQuery.isReady) return;
-			if ( document.readyState != "loaded" && document.readyState != "complete" ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			if ( numStyles === undefined )
-				numStyles = jQuery("style, link[rel=stylesheet]").length;
-			if ( document.styleSheets.length != numStyles ) {
-				setTimeout( arguments.callee, 0 );
-				return;
-			}
-			// and execute any waiting functions
-			jQuery.ready();
-		})();
-	}
-
-	// A fallback to window.onload, that will always work
-	jQuery.event.add( window, "load", jQuery.ready );
-}
-
-jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
-	"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
-	"submit,keydown,keypress,keyup,error").split(","), function(i, name){
-	
-	// Handle event binding
-	jQuery.fn[name] = function(fn){
-		return fn ? this.bind(name, fn) : this.trigger(name);
-	};
-});
-
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function(event, elem) {
-	// Check if mouse(over|out) are still within the same parent element
-	var parent = event.relatedTarget;
-	// Traverse up the tree
-	while ( parent && parent != elem ) try { parent = parent.parentNode; } catch(error) { parent = elem; }
-	// Return true if we actually just moused on to a sub-element
-	return parent == elem;
-};
-
-// Prevent memory leaks in IE
-// And prevent errors on refresh with events like mouseover in other browsers
-// Window isn't included so as not to unbind existing unload events
-jQuery(window).bind("unload", function() {
-	jQuery("*").add(document).unbind();
-});
-jQuery.fn.extend({
-	load: function( url, params, callback ) {
-		if ( jQuery.isFunction( url ) )
-			return this.bind("load", url);
-
-		var off = url.indexOf(" ");
-		if ( off >= 0 ) {
-			var selector = url.slice(off, url.length);
-			url = url.slice(0, off);
-		}
-
-		callback = callback || function(){};
-
-		// Default to a GET request
-		var type = "GET";
-
-		// If the second parameter was provided
-		if ( params )
-			// If it's a function
-			if ( jQuery.isFunction( params ) ) {
-				// We assume that it's the callback
-				callback = params;
-				params = null;
-
-			// Otherwise, build a param string
-			} else {
-				params = jQuery.param( params );
-				type = "POST";
-			}
-
-		var self = this;
-
-		// Request the remote document
-		jQuery.ajax({
-			url: url,
-			type: type,
-			dataType: "html",
-			data: params,
-			complete: function(res, status){
-				// If successful, inject the HTML into all the matched elements
-				if ( status == "success" || status == "notmodified" )
-					// See if a selector was specified
-					self.html( selector ?
-						// Create a dummy div to hold the results
-						jQuery("<div/>")
-							// inject the contents of the document in, removing the scripts
-							// to avoid any 'Permission Denied' errors in IE
-							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
-
-							// Locate the specified elements
-							.find(selector) :
-
-						// If not, just inject the full result
-						res.responseText );
-
-				self.each( callback, [res.responseText, status, res] );
-			}
-		});
-		return this;
-	},
-
-	serialize: function() {
-		return jQuery.param(this.serializeArray());
-	},
-	serializeArray: function() {
-		return this.map(function(){
-			return jQuery.nodeName(this, "form") ?
-				jQuery.makeArray(this.elements) : this;
-		})
-		.filter(function(){
-			return this.name && !this.disabled && 
-				(this.checked || /select|textarea/i.test(this.nodeName) || 
-					/text|hidden|password/i.test(this.type));
-		})
-		.map(function(i, elem){
-			var val = jQuery(this).val();
-			return val == null ? null :
-				val.constructor == Array ?
-					jQuery.map( val, function(val, i){
-						return {name: elem.name, value: val};
-					}) :
-					{name: elem.name, value: val};
-		}).get();
-	}
-});
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
-	jQuery.fn[o] = function(f){
-		return this.bind(o, f);
-	};
-});
-
-var jsc = (new Date).getTime();
-
-jQuery.extend({
-	get: function( url, data, callback, type ) {
-		// shift arguments if data argument was ommited
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = null;
-		}
-		
-		return jQuery.ajax({
-			type: "GET",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	getScript: function( url, callback ) {
-		return jQuery.get(url, null, callback, "script");
-	},
-
-	getJSON: function( url, data, callback ) {
-		return jQuery.get(url, data, callback, "json");
-	},
-
-	post: function( url, data, callback, type ) {
-		if ( jQuery.isFunction( data ) ) {
-			callback = data;
-			data = {};
-		}
-
-		return jQuery.ajax({
-			type: "POST",
-			url: url,
-			data: data,
-			success: callback,
-			dataType: type
-		});
-	},
-
-	ajaxSetup: function( settings ) {
-		jQuery.extend( jQuery.ajaxSettings, settings );
-	},
-
-	ajaxSettings: {
-		global: true,
-		type: "GET",
-		timeout: 0,
-		contentType: "application/x-www-form-urlencoded",
-		processData: true,
-		async: true,
-		data: null,
-		username: null,
-		password: null,
-		accepts: {
-			xml: "application/xml, text/xml",
-			html: "text/html",
-			script: "text/javascript, application/javascript",
-			json: "application/json, text/javascript",
-			text: "text/plain",
-			_default: "*/*"
-		}
-	},
-	
-	// Last-Modified header cache for next request
-	lastModified: {},
-
-	ajax: function( s ) {
-		var jsonp, jsre = /=\?(&|$)/g, status, data;
-
-		// Extend the settings, but re-extend 's' so that it can be
-		// checked again later (in the test suite, specifically)
-		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
-
-		// convert data if not already a string
-		if ( s.data && s.processData && typeof s.data != "string" )
-			s.data = jQuery.param(s.data);
-
-		// Handle JSONP Parameter Callbacks
-		if ( s.dataType == "jsonp" ) {
-			if ( s.type.toLowerCase() == "get" ) {
-				if ( !s.url.match(jsre) )
-					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
-			} else if ( !s.data || !s.data.match(jsre) )
-				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
-			s.dataType = "json";
-		}
-
-		// Build temporary JSONP function
-		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
-			jsonp = "jsonp" + jsc++;
-
-			// Replace the =? sequence both in the query string and the data
-			if ( s.data )
-				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
-			s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
-			// We need to make sure
-			// that a JSONP style response is executed properly
-			s.dataType = "script";
-
-			// Handle JSONP-style loading
-			window[ jsonp ] = function(tmp){
-				data = tmp;
-				success();
-				complete();
-				// Garbage collect
-				window[ jsonp ] = undefined;
-				try{ delete window[ jsonp ]; } catch(e){}
-				if ( head )
-					head.removeChild( script );
-			};
-		}
-
-		if ( s.dataType == "script" && s.cache == null )
-			s.cache = false;
-
-		if ( s.cache === false && s.type.toLowerCase() == "get" ) {
-			var ts = (new Date()).getTime();
-			// try replacing _= if it is there
-			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
-			// if nothing was replaced, add timestamp to the end
-			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
-		}
-
-		// If data is available, append data to url for get requests
-		if ( s.data && s.type.toLowerCase() == "get" ) {
-			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
-
-			// IE likes to send both get and post data, prevent this
-			s.data = null;
-		}
-
-		// Watch for a new set of requests
-		if ( s.global && ! jQuery.active++ )
-			jQuery.event.trigger( "ajaxStart" );
-
-		// If we're requesting a remote document
-		// and trying to load JSON or Script with a GET
-		if ( (!s.url.indexOf("http") || !s.url.indexOf("//")) && s.dataType == "script" && s.type.toLowerCase() == "get" ) {
-			var head = document.getElementsByTagName("head")[0];
-			var script = document.createElement("script");
-			script.src = s.url;
-			if (s.scriptCharset)
-				script.charset = s.scriptCharset;
-
-			// Handle Script loading
-			if ( !jsonp ) {
-				var done = false;
-
-				// Attach handlers for all browsers
-				script.onload = script.onreadystatechange = function(){
-					if ( !done && (!this.readyState || 
-							this.readyState == "loaded" || this.readyState == "complete") ) {
-						done = true;
-						success();
-						complete();
-						head.removeChild( script );
-					}
-				};
-			}
-
-			head.appendChild(script);
-
-			// We handle everything using the script element injection
-			return undefined;
-		}
-
-		var requestDone = false;
-
-		// Create the request object; Microsoft failed to properly
-		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
-		var xml = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
-
-		// Open the socket
-		xml.open(s.type, s.url, s.async, s.username, s.password);
-
-		// Need an extra try/catch for cross domain requests in Firefox 3
-		try {
-			// Set the correct header, if data is being sent
-			if ( s.data )
-				xml.setRequestHeader("Content-Type", s.contentType);
-
-			// Set the If-Modified-Since header, if ifModified mode.
-			if ( s.ifModified )
-				xml.setRequestHeader("If-Modified-Since",
-					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
-
-			// Set header so the called script knows that it's an XMLHttpRequest
-			xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-
-			// Set the Accepts header for the server, depending on the dataType
-			xml.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
-				s.accepts[ s.dataType ] + ", */*" :
-				s.accepts._default );
-		} catch(e){}
-
-		// Allow custom headers/mimetypes
-		if ( s.beforeSend )
-			s.beforeSend(xml);
-			
-		if ( s.global )
-			jQuery.event.trigger("ajaxSend", [xml, s]);
-
-		// Wait for a response to come back
-		var onreadystatechange = function(isTimeout){
-			// The transfer is complete and the data is available, or the request timed out
-			if ( !requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
-				requestDone = true;
-				
-				// clear poll interval
-				if (ival) {
-					clearInterval(ival);
-					ival = null;
-				}
-				
-				status = isTimeout == "timeout" && "timeout" ||
-					!jQuery.httpSuccess( xml ) && "error" ||
-					s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
-					"success";
-
-				if ( status == "success" ) {
-					// Watch for, and catch, XML document parse errors
-					try {
-						// process the data (runs the xml through httpData regardless of callback)
-						data = jQuery.httpData( xml, s.dataType );
-					} catch(e) {
-						status = "parsererror";
-					}
-				}
-
-				// Make sure that the request was successful or notmodified
-				if ( status == "success" ) {
-					// Cache Last-Modified header, if ifModified mode.
-					var modRes;
-					try {
-						modRes = xml.getResponseHeader("Last-Modified");
-					} catch(e) {} // swallow exception thrown by FF if header is not available
-	
-					if ( s.ifModified && modRes )
-						jQuery.lastModified[s.url] = modRes;
-
-					// JSONP handles its own success callback
-					if ( !jsonp )
-						success();	
-				} else
-					jQuery.handleError(s, xml, status);
-
-				// Fire the complete handlers
-				complete();
-
-				// Stop memory leaks
-				if ( s.async )
-					xml = null;
-			}
-		};
-		
-		if ( s.async ) {
-			// don't attach the handler to the request, just poll it instead
-			var ival = setInterval(onreadystatechange, 13); 
-
-			// Timeout checker
-			if ( s.timeout > 0 )
-				setTimeout(function(){
-					// Check to see if the request is still happening
-					if ( xml ) {
-						// Cancel the request
-						xml.abort();
-	
-						if( !requestDone )
-							onreadystatechange( "timeout" );
-					}
-				}, s.timeout);
-		}
-			
-		// Send the data
-		try {
-			xml.send(s.data);
-		} catch(e) {
-			jQuery.handleError(s, xml, null, e);
-		}
-		
-		// firefox 1.5 doesn't fire statechange for sync requests
-		if ( !s.async )
-			onreadystatechange();
-
-		function success(){
-			// If a local callback was specified, fire it and pass it the data
-			if ( s.success )
-				s.success( data, status );
-
-			// Fire the global callback
-			if ( s.global )
-				jQuery.event.trigger( "ajaxSuccess", [xml, s] );
-		}
-
-		function complete(){
-			// Process result
-			if ( s.complete )
-				s.complete(xml, status);
-
-			// The request was completed
-			if ( s.global )
-				jQuery.event.trigger( "ajaxComplete", [xml, s] );
-
-			// Handle the global AJAX counter
-			if ( s.global && ! --jQuery.active )
-				jQuery.event.trigger( "ajaxStop" );
-		}
-		
-		// return XMLHttpRequest to allow aborting the request etc.
-		return xml;
-	},
-
-	handleError: function( s, xml, status, e ) {
-		// If a local callback was specified, fire it
-		if ( s.error ) s.error( xml, status, e );
-
-		// Fire the global callback
-		if ( s.global )
-			jQuery.event.trigger( "ajaxError", [xml, s, e] );
-	},
-
-	// Counter for holding the number of active queries
-	active: 0,
-
-	// Determines if an XMLHttpRequest was successful or not
-	httpSuccess: function( r ) {
-		try {
-			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
-			return !r.status && location.protocol == "file:" ||
-				( r.status >= 200 && r.status < 300 ) || r.status == 304 || r.status == 1223 ||
-				jQuery.browser.safari && r.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	// Determines if an XMLHttpRequest returns NotModified
-	httpNotModified: function( xml, url ) {
-		try {
-			var xmlRes = xml.getResponseHeader("Last-Modified");
-
-			// Firefox always returns 200. check Last-Modified date
-			return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
-				jQuery.browser.safari && xml.status == undefined;
-		} catch(e){}
-		return false;
-	},
-
-	httpData: function( r, type ) {
-		var ct = r.getResponseHeader("content-type");
-		var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
-		var data = xml ? r.responseXML : r.responseText;
-
-		if ( xml && data.documentElement.tagName == "parsererror" )
-			throw "parsererror";
-
-		// If the type is "script", eval it in global context
-		if ( type == "script" )
-			jQuery.globalEval( data );
-
-		// Get the JavaScript object, if JSON is used.
-		if ( type == "json" )
-			data = eval("(" + data + ")");
-
-		return data;
-	},
-
-	// Serialize an array of form elements or a set of
-	// key/values into a query string
-	param: function( a ) {
-		var s = [];
-
-		// If an array was passed in, assume that it is an array
-		// of form elements
-		if ( a.constructor == Array || a.jquery )
-			// Serialize the form elements
-			jQuery.each( a, function(){
-				s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
-			});
-
-		// Otherwise, assume that it's an object of key/value pairs
-		else
-			// Serialize the key/values
-			for ( var j in a )
-				// If the value is an array then the key names need to be repeated
-				if ( a[j] && a[j].constructor == Array )
-					jQuery.each( a[j], function(){
-						s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
-					});
-				else
-					s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
-
-		// Return the resulting serialization
-		return s.join("&").replace(/%20/g, "+");
-	}
-
-});
-jQuery.fn.extend({
-	show: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "show", width: "show", opacity: "show"
-			}, speed, callback) :
-			
-			this.filter(":hidden").each(function(){
-				this.style.display = this.oldblock || "";
-				if ( jQuery.css(this,"display") == "none" ) {
-					var elem = jQuery("<" + this.tagName + " />").appendTo("body");
-					this.style.display = elem.css("display");
-					// handle an edge condition where css is - div { display:none; } or similar
-					if (this.style.display == "none")
-						this.style.display = "block";
-					elem.remove();
-				}
-			}).end();
-	},
-	
-	hide: function(speed,callback){
-		return speed ?
-			this.animate({
-				height: "hide", width: "hide", opacity: "hide"
-			}, speed, callback) :
-			
-			this.filter(":visible").each(function(){
-				this.oldblock = this.oldblock || jQuery.css(this,"display");
-				this.style.display = "none";
-			}).end();
-	},
-
-	// Save the old toggle function
-	_toggle: jQuery.fn.toggle,
-	
-	toggle: function( fn, fn2 ){
-		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
-			this._toggle( fn, fn2 ) :
-			fn ?
-				this.animate({
-					height: "toggle", width: "toggle", opacity: "toggle"
-				}, fn, fn2) :
-				this.each(function(){
-					jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
-				});
-	},
-	
-	slideDown: function(speed,callback){
-		return this.animate({height: "show"}, speed, callback);
-	},
-	
-	slideUp: function(speed,callback){
-		return this.animate({height: "hide"}, speed, callback);
-	},
-
-	slideToggle: function(speed, callback){
-		return this.animate({height: "toggle"}, speed, callback);
-	},
-	
-	fadeIn: function(speed, callback){
-		return this.animate({opacity: "show"}, speed, callback);
-	},
-	
-	fadeOut: function(speed, callback){
-		return this.animate({opacity: "hide"}, speed, callback);
-	},
-	
-	fadeTo: function(speed,to,callback){
-		return this.animate({opacity: to}, speed, callback);
-	},
-	
-	animate: function( prop, speed, easing, callback ) {
-		var optall = jQuery.speed(speed, easing, callback);
-
-		return this[ optall.queue === false ? "each" : "queue" ](function(){
-			if ( this.nodeType != 1)
-				return false;
-
-			var opt = jQuery.extend({}, optall);
-			var hidden = jQuery(this).is(":hidden"), self = this;
-			
-			for ( var p in prop ) {
-				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
-					return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
-
-				if ( p == "height" || p == "width" ) {
-					// Store display property
-					opt.display = jQuery.css(this, "display");
-
-					// Make sure that nothing sneaks out
-					opt.overflow = this.style.overflow;
-				}
-			}
-
-			if ( opt.overflow != null )
-				this.style.overflow = "hidden";
-
-			opt.curAnim = jQuery.extend({}, prop);
-			
-			jQuery.each( prop, function(name, val){
-				var e = new jQuery.fx( self, opt, name );
-
-				if ( /toggle|show|hide/.test(val) )
-					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
-				else {
-					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
-						start = e.cur(true) || 0;
-
-					if ( parts ) {
-						var end = parseFloat(parts[2]),
-							unit = parts[3] || "px";
-
-						// We need to compute starting value
-						if ( unit != "px" ) {
-							self.style[ name ] = (end || 1) + unit;
-							start = ((end || 1) / e.cur(true)) * start;
-							self.style[ name ] = start + unit;
-						}
-
-						// If a +=/-= token was provided, we're doing a relative animation
-						if ( parts[1] )
-							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
-
-						e.custom( start, end, unit );
-					} else
-						e.custom( start, val, "" );
-				}
-			});
-
-			// For JS strict compliance
-			return true;
-		});
-	},
-	
-	queue: function(type, fn){
-		if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
-			fn = type;
-			type = "fx";
-		}
-
-		if ( !type || (typeof type == "string" && !fn) )
-			return queue( this[0], type );
-
-		return this.each(function(){
-			if ( fn.constructor == Array )
-				queue(this, type, fn);
-			else {
-				queue(this, type).push( fn );
-			
-				if ( queue(this, type).length == 1 )
-					fn.apply(this);
-			}
-		});
-	},
-
-	stop: function(clearQueue, gotoEnd){
-		var timers = jQuery.timers;
-
-		if (clearQueue)
-			this.queue([]);
-
-		this.each(function(){
-			// go in reverse order so anything added to the queue during the loop is ignored
-			for ( var i = timers.length - 1; i >= 0; i-- )
-				if ( timers[i].elem == this ) {
-					if (gotoEnd)
-						// force the next step to be the last
-						timers[i](true);
-					timers.splice(i, 1);
-				}
-		});
-
-		// start the next in the queue if the last step wasn't forced
-		if (!gotoEnd)
-			this.dequeue();
-
-		return this;
-	}
-
-});
-
-var queue = function( elem, type, array ) {
-	if ( !elem )
-		return undefined;
-
-	type = type || "fx";
-
-	var q = jQuery.data( elem, type + "queue" );
-
-	if ( !q || array )
-		q = jQuery.data( elem, type + "queue", 
-			array ? jQuery.makeArray(array) : [] );
-
-	return q;
-};
-
-jQuery.fn.dequeue = function(type){
-	type = type || "fx";
-
-	return this.each(function(){
-		var q = queue(this, type);
-
-		q.shift();
-
-		if ( q.length )
-			q[0].apply( this );
-	});
-};
-
-jQuery.extend({
-	
-	speed: function(speed, easing, fn) {
-		var opt = speed && speed.constructor == Object ? speed : {
-			complete: fn || !fn && easing || 
-				jQuery.isFunction( speed ) && speed,
-			duration: speed,
-			easing: fn && easing || easing && easing.constructor != Function && easing
-		};
-
-		opt.duration = (opt.duration && opt.duration.constructor == Number ? 
-			opt.duration : 
-			{ slow: 600, fast: 200 }[opt.duration]) || 400;
-	
-		// Queueing
-		opt.old = opt.complete;
-		opt.complete = function(){
-			if ( opt.queue !== false )
-				jQuery(this).dequeue();
-			if ( jQuery.isFunction( opt.old ) )
-				opt.old.apply( this );
-		};
-	
-		return opt;
-	},
-	
-	easing: {
-		linear: function( p, n, firstNum, diff ) {
-			return firstNum + diff * p;
-		},
-		swing: function( p, n, firstNum, diff ) {
-			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
-		}
-	},
-	
-	timers: [],
-	timerId: null,
-
-	fx: function( elem, options, prop ){
-		this.options = options;
-		this.elem = elem;
-		this.prop = prop;
-
-		if ( !options.orig )
-			options.orig = {};
-	}
-
-});
-
-jQuery.fx.prototype = {
-
-	// Simple function for setting a style value
-	update: function(){
-		if ( this.options.step )
-			this.options.step.apply( this.elem, [ this.now, this ] );
-
-		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
-
-		// Set display property to block for height/width animations
-		if ( this.prop == "height" || this.prop == "width" )
-			this.elem.style.display = "block";
-	},
-
-	// Get the current size
-	cur: function(force){
-		if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
-			return this.elem[ this.prop ];
-
-		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
-		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
-	},
-
-	// Start an animation from one number to another
-	custom: function(from, to, unit){
-		this.startTime = (new Date()).getTime();
-		this.start = from;
-		this.end = to;
-		this.unit = unit || this.unit || "px";
-		this.now = this.start;
-		this.pos = this.state = 0;
-		this.update();
-
-		var self = this;
-		function t(gotoEnd){
-			return self.step(gotoEnd);
-		}
-
-		t.elem = this.elem;
-
-		jQuery.timers.push(t);
-
-		if ( jQuery.timerId == null ) {
-			jQuery.timerId = setInterval(function(){
-				var timers = jQuery.timers;
-				
-				for ( var i = 0; i < timers.length; i++ )
-					if ( !timers[i]() )
-						timers.splice(i--, 1);
-
-				if ( !timers.length ) {
-					clearInterval( jQuery.timerId );
-					jQuery.timerId = null;
-				}
-			}, 13);
-		}
-	},
-
-	// Simple 'show' function
-	show: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.show = true;
-
-		// Begin the animation
-		this.custom(0, this.cur());
-
-		// Make sure that we start at a small width/height to avoid any
-		// flash of content
-		if ( this.prop == "width" || this.prop == "height" )
-			this.elem.style[this.prop] = "1px";
-		
-		// Start by showing the element
-		jQuery(this.elem).show();
-	},
-
-	// Simple 'hide' function
-	hide: function(){
-		// Remember where we started, so that we can go back to it later
-		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-		this.options.hide = true;
-
-		// Begin the animation
-		this.custom(this.cur(), 0);
-	},
-
-	// Each step of an animation
-	step: function(gotoEnd){
-		var t = (new Date()).getTime();
-
-		if ( gotoEnd || t > this.options.duration + this.startTime ) {
-			this.now = this.end;
-			this.pos = this.state = 1;
-			this.update();
-
-			this.options.curAnim[ this.prop ] = true;
-
-			var done = true;
-			for ( var i in this.options.curAnim )
-				if ( this.options.curAnim[i] !== true )
-					done = false;
-
-			if ( done ) {
-				if ( this.options.display != null ) {
-					// Reset the overflow
-					this.elem.style.overflow = this.options.overflow;
-				
-					// Reset the display
-					this.elem.style.display = this.options.display;
-					if ( jQuery.css(this.elem, "display") == "none" )
-						this.elem.style.display = "block";
-				}
-
-				// Hide the element if the "hide" operation was done
-				if ( this.options.hide )
-					this.elem.style.display = "none";
-
-				// Reset the properties, if the item has been hidden or shown
-				if ( this.options.hide || this.options.show )
-					for ( var p in this.options.curAnim )
-						jQuery.attr(this.elem.style, p, this.options.orig[p]);
-			}
-
-			// If a callback was provided, execute it
-			if ( done && jQuery.isFunction( this.options.complete ) )
-				// Execute the complete function
-				this.options.complete.apply( this.elem );
-
-			return false;
-		} else {
-			var n = t - this.startTime;
-			this.state = n / this.options.duration;
-
-			// Perform the easing function, defaults to swing
-			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
-			this.now = this.start + ((this.end - this.start) * this.pos);
-
-			// Perform the next step of the animation
-			this.update();
-		}
-
-		return true;
-	}
-
-};
-
-jQuery.fx.step = {
-	scrollLeft: function(fx){
-		fx.elem.scrollLeft = fx.now;
-	},
-
-	scrollTop: function(fx){
-		fx.elem.scrollTop = fx.now;
-	},
-
-	opacity: function(fx){
-		jQuery.attr(fx.elem.style, "opacity", fx.now);
-	},
-
-	_default: function(fx){
-		fx.elem.style[ fx.prop ] = fx.now + fx.unit;
-	}
-};
-// The Offset Method
-// Originally By Brandon Aaron, part of the Dimension Plugin
-// http://jquery.com/plugins/project/dimensions
-jQuery.fn.offset = function() {
-	var left = 0, top = 0, elem = this[0], results;
-	
-	if ( elem ) with ( jQuery.browser ) {
-		var parent       = elem.parentNode, 
-		    offsetChild  = elem,
-		    offsetParent = elem.offsetParent, 
-		    doc          = elem.ownerDocument,
-		    safari2      = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
-		    fixed        = jQuery.css(elem, "position") == "fixed";
-	
-		// Use getBoundingClientRect if available
-		if ( elem.getBoundingClientRect ) {
-			var box = elem.getBoundingClientRect();
-		
-			// Add the document scroll offsets
-			add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-				box.top  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		
-			// IE adds the HTML element's border, by default it is medium which is 2px
-			// IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; }
-			// IE 7 standards mode, the border is always 2px
-			// This border/offset is typically represented by the clientLeft and clientTop properties
-			// However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
-			// Therefore this method will be off by 2px in IE while in quirksmode
-			add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
-	
-		// Otherwise loop through the offsetParents and parentNodes
-		} else {
-		
-			// Initial element offsets
-			add( elem.offsetLeft, elem.offsetTop );
-			
-			// Get parent offsets
-			while ( offsetParent ) {
-				// Add offsetParent offsets
-				add( offsetParent.offsetLeft, offsetParent.offsetTop );
-			
-				// Mozilla and Safari > 2 does not include the border on offset parents
-				// However Mozilla adds the border for table or table cells
-				if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
-					border( offsetParent );
-					
-				// Add the document scroll offsets if position is fixed on any offsetParent
-				if ( !fixed && jQuery.css(offsetParent, "position") == "fixed" )
-					fixed = true;
-			
-				// Set offsetChild to previous offsetParent unless it is the body element
-				offsetChild  = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
-				// Get next offsetParent
-				offsetParent = offsetParent.offsetParent;
-			}
-		
-			// Get parent scroll offsets
-			while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) ) {
-				// Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
-				if ( !/^inline|table.*$/i.test(jQuery.css(parent, "display")) )
-					// Subtract parent scroll offsets
-					add( -parent.scrollLeft, -parent.scrollTop );
-			
-				// Mozilla does not add the border for a parent that has overflow != visible
-				if ( mozilla && jQuery.css(parent, "overflow") != "visible" )
-					border( parent );
-			
-				// Get next parent
-				parent = parent.parentNode;
-			}
-		
-			// Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
-			// Mozilla doubles body offsets with a non-absolutely positioned offsetChild
-			if ( (safari2 && (fixed || jQuery.css(offsetChild, "position") == "absolute")) || 
-				(mozilla && jQuery.css(offsetChild, "position") != "absolute") )
-					add( -doc.body.offsetLeft, -doc.body.offsetTop );
-			
-			// Add the document scroll offsets if position is fixed
-			if ( fixed )
-				add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
-					Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
-		}
-
-		// Return an object with top and left properties
-		results = { top: top, left: left };
-	}
-
-	function border(elem) {
-		add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) );
-	}
-
-	function add(l, t) {
-		left += parseInt(l) || 0;
-		top += parseInt(t) || 0;
-	}
-
-	return results;
-};
-})();
@@ -1 +0,0 @@
-Howdy <! name !>. How are you?
@@ -1 +0,0 @@
-Hola <! name !>. Como estas?
@@ -1,3 +0,0 @@
-English: <! INCLUDE greeting/english !>
-
-Spanish: <! InCluDe greeting/spanish !>
@@ -1,5 +0,0 @@
-<! BLOCK importantMessage !>
-    Message is <b><! message !></b>
-<! END !>
-<!- CLEAR !>
-Hello, <! name !>!
@@ -1,76 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(6);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Basic Substitution1
---- context
-{"name":"Wally"}
---- jemplate
-hello.html
-Hello, [% name %]
---- output
-Hello, Wally
-
-=== Basic Substitution2
---- context
-{"name":"Yann"}
---- jemplate: hello.html
---- output
-Hello, Yann
-
-=== Operator "=="
---- jemplate
-operator1.html
-[%- IF "abc" == "abc" -%]
-same
-[%- END %]
---- output
-same
-
-=== Operator "!="
---- jemplate
-operator2.html
-[%- IF "abc" != "def" -%]
-not same
-[%- END -%]
-
---- output
-not same
-
-=== Operator concat "+"
---- jemplate
-operator3.html
-[%- "abc" _ "def" -%]
-
---- output
-abcdef
-
-=== Array index fetch"
---- jemplate
-basic_array1.html
-[%- JAVASCRIPT -%]
-stash.set( "simple_list", ["a","b","c"] );
-stash.set( "mylist", [["a","b","c"],["d","e","f"],["h","i","j"]] );
-[%- END -%]
-a = [% simple_list.0 %]
-a = [% mylist.0.0 %]
-c = [% mylist.0.2 %]
-e = [% mylist.1.1 %]
-j = [% mylist.2.2 -%]
-
---- output
-a = a
-a = a
-c = c
-e = e
-j = j
-
-*/
@@ -1,56 +0,0 @@
-var t = new Test.Jemplate();
-t.plan(1);
-t.pass('XXX - Finish writing this test file');
-
-/* Test
-=== Foo
---- xxx
-yyy
-
-*/
-// NODE CLASS
-
-/*
-=item * val = getName()
-
-This returns the name assigned to this object by the serverside code.
-
-If no name has been set, then this will return "E<lt>no nameE<gt>".
-
-*/
-
-/*
-,getName: function () { return this.name ? this.name : '<no name>'; } */
-
-/*
-
------------------------------------------
-// JEMPLATE CALL
-
-while(!iterator.atEnd()) {
-  var node = iterator.get();
-  tableBody += Jemplate.process('nl_table_row.html', { node: node, mode: this.mode });
-}
-this.viewer.innerHTML += tableBody;
-
-------------------------------------------
-// TEMPLATE
-
-<tr>
-  <td>
-    <a href="" id="node-[% node.id %]-list-item">
-    [% node.getName() %] - [% node.name %]
-    </a>
-  </td>
-  [% IF mode == 'updated' %]
-  <td id="node-[% node.id %]-list-timestamp">
-    [% node.updated_on_formatted %]
-  </td>
-  [% END %]
-</tr>
-
--------------------------------------------
-
-// node.getName() outputs nothing, but node.name outputs correctly. the getName() method definitely works.
-
-*/
@@ -1,24 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== DEFAULT directive works
---- context
-{"foo":"one"}
---- jemplate
-default.html
-[% DEFAULT foo = 'two', bar = 'three' -%]
-[% DEFAULT bar = 'four' -%]
-[% foo %] | [% bar %]
---- output
-one | three
-
-*/
@@ -1,74 +0,0 @@
-
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(5);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test BLOCK/SET/FOR/PROCESS
---- jemplate
-directives1.html
-[% BLOCK foo -%]
-I <3 Sushi
-[% END -%]
-[% SET list = [3, 4, 5] -%]
-[% FOR i = list -%]
-[% PROCESS foo -%]
-[% END -%]
---- output
-I <3 Sushi
-I <3 Sushi
-I <3 Sushi
-
-=== Test WHILE/IF/ELSE
---- jemplate
-directives2.html
-[% SET num = 4 -%]
-[% array = [] -%]
-[% WHILE num < 7 -%]
-[% IF num % 2 %][% CALL array.push('Odd') %][% ELSE %][% CALL array.push('Even') %][% END -%]
-[% num = num + 1 -%]
-[% END -%]
-[% array.join('***') %]
---- output
-Even***Odd***Even
-
-=== Test JAVASCRIPT
---- jemplate
-directives3.html
-[%- JAVASCRIPT -%]
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-[%- END -%]
-Key1: [% obj.key1 %]
-Key2: [% obj.key2 %]
---- output
-Key1: val1
-Key2: val2
-
-=== Test FOR i IN obj
---- jemplate
-directives4.html
-[%- JAVASCRIPT -%]
-stash.set("obj", {"key1": "val1", "key2": "val2"});
-[%- END -%]
-[%- FOR key IN obj -%]
-[% key %]: [% obj.$key %]
-[% END -%]
---- output
-key1: val1
-key2: val2
-=== Test NEXT
---- jemplate
-directives5.html
-[%- FOR i IN [1,2,3,4,5,6] -%]
-[%- NEXT IF i % 2 == 0 -%]
-I = [% i %];
-[%- END -%]
---- output
-I = 1;I = 3;I = 5;
-*/
@@ -1,167 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-/*
-#     Failed test
-#          got: "my%20file.html
-# my%3Cfile%20%26%20your%3Efile.html
-# my%3Cfile%20%26%20your%3Efile.html
-# guitar%26amp%3Bfile.html
-# guitar%26amp%3Bfile.html
-# "
-#     expected: "my%20file.html
-# my%3Cfile%20&amp;%20your%3Efile.html
-# my%3Cfile%20&amp;amp;%20your%3Efile.html
-# guitar&amp;amp;file.html
-# guitar&amp;amp;amp;file.html
-# "
-
-my%20file.html
-my%3Cfile%20&%20your%3Efile.html
-my%3Cfile%20&amp;%20your%3Efile.html
-guitar&amp;file.html
-guitar&amp;amp;file.html
-
-*/
-
-t.plan(7);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test indent
---- jemplate
-filters_indent.html
-[% FILTER indent -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent(3) -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent('2') -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% FILTER indent(0) -%]
-1
-2
-3
-4
-[%- END %]
-#
-[% text = 'The cat sat on the mat';
-   text | indent('> ') | indent('+') %]
---- output
-    1
-    2
-    3
-    4
-#
-   1
-   2
-   3
-   4
-#
-  1
-  2
-  3
-  4
-#
-1
-2
-3
-4
-#
-+> The cat sat on the mat
-=== Test truncate
---- jemplate
-filters_truncate.html
-[% a = '1234567890' -%]
-[% a | truncate(5)  %]
-[% a | truncate(10) %]
-[% a | truncate(15) %]
-[% a | truncate(2)  %]
-[% a = '1234567890123456789012345678901234567890' -%]
-[% a | truncate  %]
---- output
-12...
-1234567...
-1234567890
-...
-12345678901234567890123456789...
-
-=== Test null
---- jemplate
-filters_null.html
-[%- "Ils ont les chapeaux ronds, vive la bretagne" | null -%]
---- output
-=== Test uri
---- jemplate
-filters_uri.html
-[% "my file.html" FILTER uri %]
-[% "my<file & your>file.html" FILTER uri %]
-[% "my<file & your>file.html" | uri | html %]
-[% "guitar&amp;file.html" | uri %]
-[% "guitar&amp;file.html" | uri | html %]
---- output
-my%20file.html
-my%3Cfile%20%26%20your%3Efile.html
-my%3Cfile%20%26%20your%3Efile.html
-guitar%26amp%3Bfile.html
-guitar%26amp%3Bfile.html
-=== Test html 
---- jemplate
-filters_html.html
-[% FILTER html %]This is some html text
-All the <tags> should be escaped & protected
-[% END %]
-[% text = "The <cat> sat on the <mat>" %]
-[% text FILTER html %]
-[% FILTER html %]
-"It isn't what I expected", he replied.[% END %]
---- output
-This is some html text
-All the &lt;tags&gt; should be escaped &amp; protected
-
-
-The &lt;cat&gt; sat on the &lt;mat&gt;
-
-&quot;It isn't what I expected&quot;, he replied.
-
-=== Test repeat
---- jemplate
-filters_repeat.html
-[% "foo..." FILTER repeat(5) %]
---- output
-foo...foo...foo...foo...foo...
-
-=== Test replace
---- jemplate
-filters_replace.html
-[%- text = "The cat sat on the mat" %]
-[%- text FILTER replace(' ', '_') %]
-[% text FILTER replace('sat', 'shat') +%]
-[% text | replace('at', 'plat') +%]
-[% text = 'The <=> operator, blah, blah' %]
-[%- text | html | replace('blah', 'rhubarb') %]
---- output
-The_cat_sat_on_the_mat
-The cat shat on the mat
-The cplat splat on the mplat
-The &lt;=&gt; operator, rhubarb, rhubarb
-
-*/
@@ -1,23 +0,0 @@
-var t = new Test.Jemplate();
-
-t.plan(1);
-
-t.compile();
-
-var j = new Jemplate();
-
-var test1 = t.state.blocks.shift();
-t.is(
-    j.process('greetings', {name: 'Jesus'}),
-    test1.data.output,
-    test1.name
-);
-
-/* Test
-=== Include files with path names.
---- output
-English: Howdy Jesus. How are you?
-
-Spanish: Hola Jesus. Como estas?
-
-*/
@@ -1,119 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(9);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test each method
---- jemplate
-hash_each.html
-[%# This is a bit convoluted because sort order can not be guaranteed -%]
-[%# please fix it if you can find a better way... -%]
-[%# There are a few other tests below that are very similar -%]
-[% SET hash = { a=1 b=2 c=3 };
-   SET list = hash.each;
-   FOR kindex IN [0,2,4];
-     SET vindex = kindex + 1;
-     SET key = list.${kindex};
-     SET value = list.${vindex};
-     (key AND hash.${key} == value) ? 1 : 0;
-   END %]
---- output
-111
-
-=== Test exists method
---- jemplate
-hash_exists.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.exists('b') ? 1 : 0 %]
-[% a.exists('z') ? 1 : 0 %]
---- output
-1
-0
-
-=== Test import method TODO
---- jemplate
-hash_import.html
-[%# SET a = { a=1 b=2 c=3 } -%]
-[%# CALL a.import({ a=2 b=1 d=4 e=5 }) -%]
-[%# a.exists('c') ? 1 : 0 %]1
-[%# a.exists('d') ? 1 : 0 %]1
-[%# a.a %]2
-[%# a.e %]5
---- output
-1
-1
-2
-5
-
-=== Test keys method
---- jemplate
-hash_keys.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.keys.sort.join(' ') %]
---- output
-a b c
-
-=== Test list method
---- jemplate
-hash_list.html
-[% SET hash = { a=1 b=2 c=3 } -%]
-[% hash.list('keys').sort.join(' ') %]
-[% hash.list('values').sort.join(' ') %]
-[% SET list = hash.list('each');
-   FOR kindex IN [0,2,4];
-     SET vindex = kindex + 1;
-     SET key = list.${kindex};
-     SET value = list.${vindex};
-     (key AND hash.${key} == value) ? 1 : 0;
-   END %]
-[% SET list = hash.list();
-   FOR entry IN list;
-     SET key = entry.key;
-     SET value = entry.value;
-     (hash.${key} == value) ? 1 : 0;
-   END %]
---- output
-a b c
-1 2 3
-111
-111
-
-=== Test nsort method
---- jemplate
-hash_nsort.html
-[% SET a = { '499'='c' '5'='a' '50'='b' } -%]
-[% a.nsort.join(' ') %]
---- output
-5 50 499
-
-=== Test size method
---- jemplate
-hash_size.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.size %]
---- output
-3
-
-=== Test sort method
---- jemplate
-hash_sort.html
-[% SET a = { ac=1 b=2 aa=3 } -%]
-[% a.sort.join(' ') %]
---- output
-aa ac b
-
-=== Test values method
---- jemplate
-hash_values.html
-[% SET a = { a=1 b=2 c=3 } -%]
-[% a.values.nsort.join(' ') %]
---- output
-1 2 3
-
-*/
@@ -1,43 +0,0 @@
-var t = new Test.Jemplate();
-
-t.plan(3);
-
-t.compile();
-
-var j = new Jemplate();
-
-var test1 = t.state.blocks.shift();
-t.is(
-    j.process('hello', {name: 'Wally'}),
-    test1.data.output,
-    test1.name
-);
-
-var test2 = t.state.blocks.shift();
-t.is(
-    j.process('hello'),
-    test2.data.output,
-    test2.name
-);
-
-var j = new Jemplate({DEBUG_UNDEF: true});
-try {
-    j.process('hello', {namexxx: 'Bogey'});
-}
-catch(e) {
-    t.is(
-        String(e),
-        "undefined value found while using DEGUG_UNDEF",
-        "undefined value throws error"
-    );
-}
-
-/* Test
-=== Test second jemplate file (jemplate2.js)
---- output
-Hello, Wally!
-=== Test undefined value
---- output
-Hello, !
-
-*/
@@ -1,19 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test join
---- jemplate
-join.html
-[% a = ['foo', 'bar', 'baz'] %]
-[%- a.join('::') -%]
---- output
-foo::bar::baz
-*/
@@ -1,53 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(2);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test list methods
---- jemplate
-list.html
-[% SET a1 = [ 'one', 'two', 'three'] -%]
-[% CALL a1.push('four') -%]
-[%- a1.first() %] - [% a1.last() %]
-[% a1.grep('o').join('/') %]
-[% a1.max() %]+[% a1.size() %]
-[% SET a2 = a1.reverse -%]
-[% a2.join('^') %]
-[% a2.slice(1, 3).join('*') %]
-[% SET a3 = [ 5, 9, 'x', 17, 9, 33, 12, 'x', 5] -%]
-[% a3.unique().join(',') %]
-[% a1.unshift('zero').sort().join('!') %]
-[% CALL a1.shift(); CALL a1.pop(); a1.join('_') %]
-[% CALL a3.splice(2,1); CALL a3.splice(-2,1); a3.nsort.join('~') %]
-[% SET a4 = [11, 22, 33] -%]
-[% SET a5 = [44, 55, 66] -%]
-[% SET a6 = [77, 88, 99] -%]
-[% SET a7 = a4.merge(a5, 'foo', a6) -%]
-[% a7.join("'") %]
---- output
-one - four
-one/two/four
-3+4
-four^three^two^one
-three*two
-5,9,x,17,33,12
-four!one!three!two!zero
-one_three_two
-5~5~9~9~12~17~33
-11'22'33'44'55'66'foo'77'88'99
-=== Test sort of hash
---- jemplate
-list_of_hash.html
-[% SET ll = [ {'a' => 9}, {'a' =>  1}, {'a' => 3} ] -%]
-[%- FOR hash = ll.sort('a') -%]
-[%- hash.a -%]:
-[%- END -%]
---- output
-1:3:9:
-*/
@@ -1,49 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(2);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== INCLUDE should localize
---- context
-{"thing":"foo"}
---- jemplate
-localise1.html
-[% thing %]
-[% SET thing = 'bar' -%]
-[% thing %]
-[% INCLUDE myblock -%]
-[% thing %]
-[% BLOCK myblock -%]
-[% SET thing = 'baz' -%]
-[% END -%]
---- output
-foo
-bar
-bar
-
-=== PROCESS should not localize
---- context
-{"thing":"foo"}
---- jemplate
-localise2.html
-[% thing %]
-[% SET thing = 'bar' -%]
-[% thing %]
-[% PROCESS myblock -%]
-[% thing %]
-[% BLOCK myblock -%]
-[% SET thing = 'baz' -%]
-[% END -%]
---- output
-foo
-bar
-baz
-
-*/
@@ -1,83 +0,0 @@
-
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(5);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test loop.index
---- jemplate
-loop1.html
-[%- FOR i IN [0,1,2,3] -%]
-[%- i -%]:[%- loop.index %]
-[% END -%]
---- output
-0:0
-1:1
-2:2
-3:3
-
-=== Test loop.first && loop.last
---- jemplate
-loop2.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- IF loop.first -%]
-[start]
-    [%- END -%]
-    [%- i -%]
-    [%- IF loop.last -%]
-[end]
-    [%- END -%]
-[%- END %]
---- output
-[start]0123[end]
-
-=== Test loop.count
---- jemplate
-loop3.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.count%]
-[% END -%]
---- output
-0:1
-1:2
-2:3
-3:4
-
-=== Test loop.size && loop.max
---- jemplate
-loop4.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.size%]:[%-loop.max%]
-[% END -%]
---- output
-0:4:3
-1:4:3
-2:4:3
-3:4:3
-
-=== Test loop.prev && loop.next
---- jemplate
-loop5.html
-[%- FOR i IN [0,1,2,3] -%]
-    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
-[% END -%]
-[%- FOR i IN {a => 0, b => 1, c => 3, d => 4 } -%]
-    [%- i -%]:[%-loop.prev%]:[%-loop.next%]
-[% END -%]
---- output
-0::1
-1:0:2
-2:1:3
-3:2:
-a::b
-b:a:c
-c:b:d
-d:c:
-
-*/
@@ -1,69 +0,0 @@
-function TheName(name) {
-    this.name = name;
-}
-
-TheName.prototype.getName = function() {
-   return this.name;
-}
-
-TheName.prototype.setName = function(name) {
-   this.name = name;
-}
-
-var theName = new TheName('larry');
-
-// Begin test
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(4);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Basic hash
---- jemplate
-myhashname.html
-[%- JAVASCRIPT -%]
-stash.set( "object", { name: 'Wally' } );
-[%- END -%]
-Hello, [% object.name %]
---- output
-Hello, Wally
-
-=== Object attribute
---- jemplate
-myobjectnameattr.html
-[%- JAVASCRIPT -%]
-stash.set( "object", new TheName('Larry') );
-[%- END -%]
-Hello, [% object.name %]
---- output
-Hello, Larry
-
-=== Object accessor
---- jemplate
-myobjectnameac.html
-[%- JAVASCRIPT -%]
-var obj = new TheName('Bally');
-stash.set( "objectName", obj.getName() );
-[%- END -%]
-Hello, [% objectName %]
---- output
-Hello, Bally
-
-=== Basic accessor2
---- jemplate
-myobjectnameac2.html
-[%- JAVASCRIPT -%]
-stash.set( "object", new TheName('Mally') );
-[%- END -%]
-Hello, [% object.getName() %]
---- output
-Hello, Mally
-
-*/
@@ -1,34 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test plugins
---- jemplate
-plugins_basic.html
-[% USE dummy('simple param') -%]
-[% dummy.simple %]
-#
-[% dummy.params('one', 'two') %]
-#
-[% dummy.what %]
-#
-[% dummy.get_what %]
-#
---- output
-This text came from the plugin
-#
-params: one, two
-#
-simple param
-#
-what: simple param
-#
-
-*/
@@ -1,23 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate'
-};
-
-t.plan(1);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Setting a value into a hash works
---- jemplate
-test.html
-[% testhash = {} -%]
-[% testkey = 'bob' -%]
-[% testhash.$testkey = 'bozo' -%]
-Value: [% testhash.$testkey %]<br />
---- output
-Value: bozo<br />
-
-*/
@@ -1,52 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process',
-    context: 'evaluate',
-    raw_context: 'raw_context'
-};
-
-t.plan(3);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Both .keys and .keys() works
---- context
-{"hash":{"foo":"FOO","bar":"BAR"}}
---- jemplate
-stash-functions1.html
-[% hash.keys.sort.join('+') %]
-[% hash.keys().sort().join('+') %]
---- output
-bar+foo
-bar+foo
-
-=== Disambiguatation of .keys and .keys()
---- context
-{"hash":{"keys":"foo","values":"bar"}}
---- jemplate
-stash-functions2.html
-[% hash.keys %]
-[% hash.keys().join('+') %]
---- output
-foo
-keys+values
-
-=== Stashed Function
---- raw_context
-{ hash: { 
-    noarg: function() { return "noarg" },
-    arg:   function(arg) { return "arg: " + arg }
-} }
---- jemplate
-stash-functions3.html
-[% hash.noarg %]
-[% hash.noarg() %]
-[% hash.arg("abc") %]
---- output
-noarg
-noarg
-arg: abc
-
-*/
@@ -1,132 +0,0 @@
-var t = new Test.Jemplate();
-
-var filters = {
-    jemplate: 'jemplate_process'
-};
-
-t.plan(10);
-t.filters(filters);
-t.run_is('jemplate', 'output');
-
-/* Test
-=== Test chunk method
---- jemplate
-string_chunk.html
-[% SET a = '1234567890' -%]
-[% a.chunk.join(' ') %]
-[% a.chunk(2).join(' ') %]
-[% a.chunk(3).join(' ') %]
-[% a.chunk(9).join(' ') %]
-[% a.chunk(10).join(' ') %]
-[% a.chunk(-1).join(' ') %]
-[% a.chunk(-2).join(' ') %]
-[% a.chunk(-3).join(' ') %]
-[% a.chunk(-9).join(' ') %]
-[% a.chunk(-10).join(' ') %]
---- output
-1 2 3 4 5 6 7 8 9 0
-12 34 56 78 90
-123 456 789 0
-123456789 0
-1234567890
-1 2 3 4 5 6 7 8 9 0
-12 34 56 78 90
-1 234 567 890
-1 234567890
-1234567890
-
-=== Test defined method
---- jemplate
-string_defined.html
-[% SET a = '1' -%]
-[% a.defined ? '1' : '0' %]
---- output
-1
-
-=== Test hash method
---- jemplate
-string_hash.html
-[% SET a = 'Hi' -%]
-[% a.hash.value %]
---- output
-Hi
-
-=== Test list method
---- jemplate
-string_list.html
-[% SET a = 'Hi' -%]
-[% a.list.0 %]
---- output
-Hi
-
-=== Test match method
---- jemplate
-string_match.html
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.match('\\\d\\\d').join(' ') %]
---- output
-12 34 56
-
-=== Test repeat method
---- jemplate
-string_repeat.html
-[% SET a = 'aaa' -%]
-[% a.repeat(3) %]
-[% a.repeat() %]
---- output
-aaaaaaaaa
-aaa
-
-=== Test replace method
---- jemplate
-string_replace.html
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.replace('\\\d\\\d', 'bb') %]
-[% a.replace('\\\d\\\d') %]
---- output
-aaabbaaabbaaabb
-aaaaaaaaa
-
-=== Test search method
---- jemplate
-string_search.html
-[% SET a = 'aaa12aaa34aaa56' -%]
-[% a.search('\\\d\\\d') ? 1 : 0 %]
-[% a.search('w') ? 1 : 0 %]
---- output
-1
-0
-
-=== Test size method
---- jemplate
-string_size.html
-[% SET a = '1' -%]
-[% a.defined ? '1' : '0' %]
---- output
-1
-
-=== Test split method
---- jemplate
-string_split.html
-[% SET a = 'aaa12aaa34aaa' -%]
-[% a.split('\\\d\\\d').join(' ') %]
-[% SET a = '1aaa2aaa3aaa4' -%]
-[% a.split('aaa').join(' ') %]
---- output
-aaa aaa aaa
-1 2 3 4
-
-*/
-
-/*
-=== Test length method
---- jemplate
-string_length.html
-[% SET a = 'Hi' -%]
-[% a.length %]
-[% SET a = 10 -%]
-[% a.length %]
---- output
-2
-
-*/
@@ -1,16 +0,0 @@
-<html>
-<head>
-    <title>[% index_title %]</title>
-    <script type="text/javascript" src="js/Test/Harness.js"></script>
-    <script type="text/javascript" src="js/Test/Harness/Browser.js"></script>
-</head>
-<body>
-<script type="text/javascript">
-    var t = new Test.Harness.Browser();
-    
-    t.runTests(
-        "[% all_test_files.join("\",\n        \"") %]"
-    );
-</script>
-</body>
-</html>
@@ -1,24 +0,0 @@
-<html>
-<head>
-    <script type="text/javascript" src="js/jquery.js"></script>
-
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo/yahoo-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/event/event-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/connection/connection-min.js"></script>
-    <script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/json/json-min.js"></script>
-
-    <script type="text/javascript" src="js/Subclass.js"></script>
-    <script type="text/javascript" src="js/Test/Builder.js"></script>
-    <script type="text/javascript" src="js/Test/Base.js"></script>
-[% FOR lib = import_libs -%]
-    <script type="text/javascript" src="[% lib %]"></script>
-[% END -%]
-    <script type="text/javascript" src="js/jemplate_dummy_plugin.js"></script>
-    <script type="text/javascript">
-        var testBaseCurrentScript = '[% test_file %]';
-    </script>
-    <script type="text/javascript" src="[% test_file %]"></script>
-</head>
-<body>
-</body>
-</html>